<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          以淘寶店鋪為例,談?wù)?TypeScript ESLint 規(guī)則

          共 10147字,需瀏覽 21分鐘

           ·

          2022-06-08 00:46


          前言

          ESLint 在項(xiàng)目中已經(jīng)是大家見慣不慣的存在,你可能很厭煩動(dòng)不動(dòng)跳出來的 ESLint 報(bào)錯(cuò),也可能很享受經(jīng)過統(tǒng)一校驗(yàn)的工工整整的代碼,無論如何,我的意見是,在稍微正式點(diǎn)的項(xiàng)目中都要有 ESLint 的存在,無論是直接使用簡(jiǎn)單的 recommend 配置如 extends: ['eslint: recommend'],還是精心研究了一整套適用于自己的規(guī)則集,Lint 工具的最大幫助就是保持語法統(tǒng)一,至少項(xiàng)目中的所有 JavaScript 文件應(yīng)使用統(tǒng)一的單雙引號(hào)、分號(hào)、縮進(jìn)等風(fēng)格(僅靠編輯器并不能保證)。

          其次,Lint 幫助你的代碼更加簡(jiǎn)潔、有效,如不允許未使用的變量、JSX/TSX 中使用簡(jiǎn)寫的 true 屬性( 而不是 )等、還有一點(diǎn)值得一提,ESLint 并不會(huì)一直嘗試去簡(jiǎn)化你的代碼,在很多情況下它會(huì)要求你寫更多代碼來換取可讀性和安全性的提升,尤其是在 TypeScript 場(chǎng)景下,explicit-module-boundary-types 規(guī)則會(huì)要求你為函數(shù)與類方法顯式的聲明其返回值,switch-exhaustiveness-check 規(guī)則會(huì)要求你處理聯(lián)合類型變量的所有類型分支。

          本文來自于我在所在團(tuán)隊(duì)(淘寶店鋪)內(nèi)部制定、落地、推廣 ESLint 規(guī)則集的收獲,將會(huì)簡(jiǎn)要的介紹一批我認(rèn)為在 TypeScript 分享中非常有必要的規(guī)則,通過這篇文章,你會(huì)了解到在制定規(guī)則時(shí)我們考慮的是什么,對(duì)于 TypeScript 代碼進(jìn)行約束的思考,以及如何在自己的團(tuán)隊(duì)內(nèi)推廣這一套規(guī)則。

          另外,淘系技術(shù)部前端架構(gòu)團(tuán)隊(duì)正在淘系內(nèi)推廣 AppLint,準(zhǔn)備將 ESLint 推廣到整個(gè)淘系前端作為 CI/CD 的卡口之一,歡迎集團(tuán)的同學(xué)了解并試用。


          基礎(chǔ)約束

          為了適應(yīng)讀者可能有的不同的約束嚴(yán)格程度,這里將規(guī)則拆分為基礎(chǔ)約束與嚴(yán)格約束部分,基礎(chǔ)約束的規(guī)則以語法統(tǒng)一(包括實(shí)際代碼與類型部分)為主,推薦所有人在所有項(xiàng)目中使用,即使是個(gè)人項(xiàng)目——說實(shí)在的,都寫 TypeScript 了,還在意這小小的 Lint 規(guī)則?而嚴(yán)格約束部分更關(guān)注類型以及 ECMAScript、TypeScript 的特殊語法,適合對(duì)代碼質(zhì)量要求較高的同學(xué)。這里不會(huì)給出推薦的錯(cuò)誤等級(jí),即使全部是 warn,只要你打開了,至少你也會(huì)在以后心情好的時(shí)候來修對(duì)吧?(對(duì)吧?)

          array-type

          TypeScript 中支持使用 ArrayT[] 的形式聲明數(shù)組類型,此規(guī)則約束項(xiàng)目中對(duì)這兩種數(shù)組類型的聲明。

          其支持的配置:

          • 僅使用 ArrayT[] 其中一種
          • 對(duì)于原始類型與類型別名使用 T[],對(duì)于對(duì)象類型、函數(shù)類型等使用 Array(推薦)

          為什么?:對(duì)于這種效果完全一致的語法,我們需要的只是確定一個(gè)規(guī)范然后在所有地方使用這一規(guī)范。實(shí)際上,這一類規(guī)則(還有后面的類型斷言語法)就類似于單引號(hào)/雙引號(hào),加不加分號(hào)這種基礎(chǔ)規(guī)則,如果你不能接受上一行代碼單引號(hào)這一行代碼雙引號(hào),那么也沒理由能接受這里一個(gè) Array 那里一個(gè) number[],另外,我個(gè)人推薦統(tǒng)一使用 []

          await-thenable

          只允許對(duì)異步函數(shù)、Promise、PromiseLike 使用 await 調(diào)用

          為什么:避免無意義的 await 調(diào)用。

          ban-ts-comment

          禁止 @ts- 指令的使用,或者允許其在提供了說明的情況下被使用,如:

          //?@ts-expect-error?這里的類型太復(fù)雜,日后補(bǔ)上
          //?@ts-nocheck?未完成遷移的文件

          此規(guī)則推薦與 prefer-ts-expect-error 搭配使用,詳見下方。

          為什么:如果說亂寫 any 叫 AnyScript,那么亂寫 @ts-ignore 就可以叫 IgnoreScript 了。

          ban-types

          禁止部分值被作為類型標(biāo)注,此規(guī)則能夠?qū)γ恳环N被禁用的類型提供特定的說明來在觸發(fā)此規(guī)則報(bào)錯(cuò)時(shí)給到良好的提示,場(chǎng)景如禁用 {}Functionobject 這一類被作為類型標(biāo)注,

          為什么?使用 {} 會(huì)讓你寸步難行:類型 {} 上不存在屬性 'foo',所以用了 {} 你大概率在下面還需要類型斷言回去或者變 any,使用 object Function 毫無意義。

          • 對(duì)于未知的對(duì)象類型,應(yīng)使用 Record
          • 對(duì)于函數(shù)類型,應(yīng)使用入?yún)ⅰ⒎祷刂当粯?biāo)注出來的具體類型:type SomeFunc = (arg1: string) => void ,或在未知的場(chǎng)景下使用 type SomeFunc = (...args: any[]) => any

          consistent-type-assertions

          TypeScript 支持通過 as<> 兩種不同的語法進(jìn)行類型斷言,如:

          const?foo?=?{}?as?Foo;
          const?foo?=?{};
          //?類似的還有常量斷言
          const?foo?=?<const>[1,?2];
          const?foo?=?[1,?2,?3]?as?const;

          這一規(guī)則約束使用統(tǒng)一的類型斷言語法,我個(gè)人一般在 Tsx 中使用 as ,在其他時(shí)候盡可能的使用 <>,原因則是 <> 更加簡(jiǎn)潔。

          為什么:類似于 array-type,做語法統(tǒng)一,但需要注意的是在 Tsx 項(xiàng)目中使用 <> 斷言會(huì)導(dǎo)致報(bào)錯(cuò),因?yàn)椴幌穹盒涂梢酝ㄟ^ 來顯式告知編譯器這里是泛型語法而非組件。

          explicit-module-boundary-types

          函數(shù)與類方法的返回值需要被顯式的指定,而不是依賴類型推導(dǎo),如:

          const?foo?=?():?Foo?=>?{};

          為什么:通過顯式指定來直觀的區(qū)分函數(shù)的功能,如副作用等,同時(shí)顯式指定的函數(shù)返回值也能在一定程度上提升 TypeScript Compiler 性能。

          no-extra-non-null-assertion

          不允許額外的重復(fù)非空斷言:

          //?x
          function?foo(bar:?number?|?undefined)?{
          ??const?bar:?number?=?bar!!!;
          }

          為什么:額,why not?

          prefer-for-of

          在你使用 for 循環(huán)遍歷數(shù)組時(shí),如果索引僅僅用來訪問數(shù)組成員,則應(yīng)該替換為 for...of

          為什么:如果不是為了兼容性場(chǎng)景,在這種場(chǎng)景下的確沒有必要使用 for 循環(huán)。

          prefer-nullish-coalescing && prefer-optional-chain

          使用 ?? 而不是 ||,使用 a?.b 而不是 a && a.b

          為什么:邏輯或 || 會(huì)將 0 與 "" 視為 false 而導(dǎo)致錯(cuò)誤的應(yīng)用默認(rèn)值,而可選鏈相比于邏輯與 && 則能夠帶來更簡(jiǎn)潔的語法(尤其是在屬性訪問嵌套多層,或值來自于一個(gè)函數(shù)時(shí),如 document.querySelector),以及與 ?? 更好的協(xié)作:const foo = a?.b?.c?.d ?? 'default';

          no-empty-interface

          不允許定義空的接口,可配置為允許單繼承下的空接口:

          //?x
          interface?Foo?{}

          //?√
          interface?Foo?extends?Bar?{}

          為什么:沒有父類型的空接口實(shí)際上就等于 {},雖然我不確定你使用它是為了什么,但我能告訴你這是不對(duì)的。而單繼承的空接口場(chǎng)景則是較多的,如先確定下繼承關(guān)系再在后續(xù)添加成員。

          no-explicit-any

          不允許顯式的 any。

          實(shí)際上這條規(guī)則只被設(shè)置為 warn 等級(jí),因?yàn)檎娴淖龅揭粋€(gè) any 不用或是全部替換成 unknown + 類型斷言 的形式成本都非常高。

          推薦配合 tsconfig 的 --noImplicitAny (檢查隱式 any)來盡可能的保證類型的完整與覆蓋率。

          no-inferrable-types

          不允許不必要的類型標(biāo)注,但可配置為允許類的屬性成員、函數(shù)的屬性成員進(jìn)行額外標(biāo)注。

          const?foo:?string?=?"linbudu";

          class?Foo?{
          ??prop1:?string?=?"linbudu";
          }

          function?foo(a:?number?=?5,?b:?boolean?=?true)?{
          ??//?...
          }

          為什么:對(duì)于普通變量來說,與實(shí)際賦值一致的類型標(biāo)注確實(shí)是沒有意義的,TypeScript 的控制流分析能很好地做到這一點(diǎn),而對(duì)于函數(shù)參數(shù)與類屬性,主要是為了確保一致性,即函數(shù)的所有參數(shù)(包括重載的各個(gè)聲明)、類的所有屬性都有類型標(biāo)注,而不是僅為沒有初始值的參數(shù)/屬性進(jìn)行標(biāo)注。

          no-non-null-asserted-nullish-coalescing

          不允許非空斷言與空值合并同時(shí)使用:bar! ?? tmp

          為什么:冗余

          no-non-null-asserted-optional-chain

          不允許非空斷言與可選鏈同時(shí)使用:foo?.bar!

          為什么:和上一條規(guī)則一樣屬于冗余,同時(shí)意味著你對(duì) ! ?? ?. 的理解存在著不當(dāng)之處。

          no-throw-literal

          不允許直接 throw 一個(gè)字符串如:throw 'err',只能拋出 Error 或基于 Error 派生類的實(shí)例,如:throw new Error('Oops!')

          為什么:拋出的 Error 實(shí)例能夠自動(dòng)的收集調(diào)用棧信息,同時(shí)借助 proposal-error-cause[3] 提案還能夠跨越調(diào)用棧來附加錯(cuò)誤原因傳遞上下文信息,不過,真的會(huì)有人直接拋出一個(gè)字符串嗎??

          no-unnecessary-type-arguments

          不允許與默認(rèn)值一致的泛型參數(shù),如:

          function?foo<T?=?number>()?{}
          foo<number>();

          為什么:出于代碼簡(jiǎn)潔考慮。

          no-unnecessary-type-assertion

          不允許與實(shí)際值一致的類型斷言,如:const foo = 'foo' as string

          為什么:你懂的。

          no-unnecessary-type-constraint

          不允許與默認(rèn)約束一致的泛型約束,如:interface FooAny {}

          為什么:同樣是出于簡(jiǎn)化代碼的考慮,在 TS 3.9 版本以后,對(duì)于未指定的泛型約束,默認(rèn)使用 unknown ,在這之前則是 any,知道這一點(diǎn)之后你就沒必要再多寫 extends unknown 了。

          non-nullable-type-assertion-style

          此規(guī)則要求在類型斷言僅起到去空值作用,如對(duì)于 string | undefined 類型斷言為 string時(shí),將其替換為非空斷言 !

          const?foo:?string?|?undefined?=?"foo";

          //?√
          foo!;
          //?x
          foo?as?string;

          為什么:當(dāng)然是因?yàn)楹?jiǎn)化代碼了!此規(guī)則的本質(zhì)是檢查經(jīng)過斷言后的類型子集是否僅剔除了空值部分,因此無需擔(dān)心對(duì)于多種有實(shí)際意義的類型分支的聯(lián)合類型誤判。

          prefer-as-const

          對(duì)于常量斷言,使用 as const 而不是 ,這一點(diǎn)類似于上面的 consistent-type-assertions 規(guī)則。

          prefer-literal-enum-member

          對(duì)于枚舉成員值,只允許使用普通字符串、數(shù)字、null、正則,而不允許變量復(fù)制、模板字符串等需要計(jì)算的操作。

          為什么:雖然 TypeScript 是允許使用各種合法表達(dá)式作為枚舉成員的,但由于枚舉的編譯結(jié)果擁有自己的作用域,因此可能導(dǎo)致錯(cuò)誤的賦值,如:

          const?imOutside?=?2;
          const?b?=?2;
          enum?Foo?{
          ??outer?=?imOutside,
          ??a?=?1,
          ??b?=?a,
          ??c?=?b,
          }

          這里 c == Foo.b == Foo.c == 1,還是 c == b == 2 ? 觀察下編譯結(jié)果:

          "use?strict";
          const?imOutside?=?2;
          const?b?=?2;
          var?Foo;
          (function?(Foo)?{
          ??Foo[(Foo["outer"]?=?imOutside)]?=?"outer";
          ??Foo[(Foo["a"]?=?1)]?=?"a";
          ??Foo[(Foo["b"]?=?1)]?=?"b";
          ??Foo[(Foo["c"]?=?1)]?=?"c";
          })(Foo?||?(Foo?=?{}));

          懂伐小老弟?

          prefer-ts-expect-error

          使用 @ts-expect-error 而不是 @ts-ignore

          為什么:@ts-ignore@ts-expect-error 二者的區(qū)別主要在于,前者是 ignore,是直接放棄了下一行的類型檢查而無論下一行是否真的有錯(cuò)誤,后者則是期望下一行確實(shí)存在一個(gè)錯(cuò)誤,并且會(huì)在下一行實(shí)際不存在錯(cuò)誤時(shí)拋出一個(gè)錯(cuò)誤。

          這一類干涉代碼檢查指令的使用本就應(yīng)該慎之又慎,在任何情況下都不應(yīng)該被作為逃生艙門(因?yàn)樗娴谋?any 還好用),如果你一定要用,也要確保用的恰當(dāng)。

          promise-function-async

          返回 Promise 的函數(shù)必須被標(biāo)記為 async,此規(guī)則能夠確保函數(shù)的調(diào)用方只需要處理 try/catch 或者 rejected promise 的情況。

          為什么:還用解釋嗎?

          嚴(yán)格約束

          no-unnecessary-boolean-literal-compare

          不允許對(duì)布爾類型變量與 true / false 的 === 比較,如:

          declare?const?someCondition:?boolean;
          if?(someCondition?===?true)?{
          }

          為什么:首先,記住我們是在寫 TypeScript,所以不要想著你的變量值還有可能是 null 所以需要這樣判斷,如果真的發(fā)生了,那么說明你的 TS 類型標(biāo)注不對(duì)哦。而且,此規(guī)則的配置項(xiàng)最多允許 boolean | null 這樣的值與 true / false 進(jìn)行比較,所以還是讓你的類型更精確一點(diǎn)吧。

          consistent-type-definitions

          TypeScript 支持通過 type 與 interface 聲明對(duì)象類型,此規(guī)則可將其收束到統(tǒng)一的聲明方式,即僅使用其中的一種。

          為什么:先說我是怎么做得:在絕大部分場(chǎng)景下,使用 interface 來聲明對(duì)象類型,type 應(yīng)當(dāng)用于聲明聯(lián)合類型、函數(shù)類型、工具類型等,如:

          interface?IFoo?{}

          type?Partial?=?{
          ??[P?in?keyof?T]?:?T[P];
          };

          type?LiteralBool?=?"true"?|?"false";

          原因主要有這么幾點(diǎn):

          • 配合 naming-convention 規(guī)則(能夠用于檢查接口是否按照規(guī)范命名),我們能夠在看見 IFoo 時(shí)立刻知道它是一個(gè) 接口,看見 Bar 時(shí)立刻知道它是一個(gè)類型別名,配置:

            {
            ??"@typescript-eslint/naming-convention":?[
            ????"error",
            ????{
            ??????"selector":?"interface",
            ??????"format":?["PascalCase"],
            ??????"custom":?{
            ????????"regex":?"^I[A-Z]",
            ????????"match":?true
            ??????}
            ????}
            ??]
            }
          • 接口在類型編程中的作用非常局限,僅支持 extends、泛型 等簡(jiǎn)單的能力,也應(yīng)當(dāng)只被用于定義確定的結(jié)構(gòu)體。而 Type Alias 能夠使用除 extends 以外所有常見的映射類型、條件類型等類型編程語法。同時(shí),“類型別名”的含義也意味著你實(shí)際上是使用它來歸類類型(聯(lián)合類型)、抽象類型(函數(shù)類型、類類型)。

          method-signature-style

          方法簽名的聲明方式有 method 與 property 兩種,區(qū)別如下:

          //?method
          interface?T1?{
          ??func(arg:?string):?number;
          }

          //?property
          interface?T2?{
          ??func:?(arg:?string)?=>?number;
          }

          此規(guī)則將聲明方式進(jìn)行約束,推薦使用第二種的 property 方式。

          為什么:首先,這兩種方式被稱為 method 與 property 很明顯是因?yàn)槠鋵?duì)應(yīng)的寫法,method 方式類似于在 Class 中定義方法,而 property 則是就像定義普通的接口屬性,只不過它的值是函數(shù)類型。推薦使用 property 的最重要原因是,通過使用 屬性 + 函數(shù)值 的方式定義,作為值的函數(shù)的類型能享受到更嚴(yán)格的類型校驗(yàn)( `strictFunctionTypes`[4]),此配置會(huì)使用逆變(contravariance)而非協(xié)變(covariance)的方式進(jìn)行函數(shù)參數(shù)的檢查,關(guān)于協(xié)變與逆變我后續(xù)會(huì)單獨(dú)的寫一篇文章,這里暫時(shí)不做展開,如果你有興趣,可以閱讀 TypeScript 類型中的逆變協(xié)變

          consistent-type-imports

          約束使用 import type {} 進(jìn)行類型的導(dǎo)入,如:

          //?√
          import?type?{?CompilerOptions?}?from?"typescript";

          //?x
          import?{?CompilerOptions?}?from?"typescript";

          為什么:import type 能夠幫助你更好的組織你的項(xiàng)目頭部的導(dǎo)入結(jié)構(gòu)(雖然 TypeScript 4.5 支持了類型與值的混合導(dǎo)入:import { foo, type Foo },但還是推薦通過拆分值導(dǎo)入與類型導(dǎo)入語句來獲得更清晰地項(xiàng)目結(jié)構(gòu))。值導(dǎo)入與類型導(dǎo)入在 TypeScript 中使用不同的堆空間來存放,因此無須擔(dān)心循環(huán)依賴(所以你可以父組件導(dǎo)入子組件,子組件導(dǎo)入定義在父組件中的類型這樣)。

          一個(gè)簡(jiǎn)單的、良好組織了導(dǎo)入語句的示例:

          import?{?useEffect?}?from?"react";

          import?{?Button,?Dialog?}?from?"ui";
          import?{?ChildComp?}?from?"./child";

          import?{?store?}?from?"@/store";
          import?{?useCookie?}?from?"@/hooks/useCookie";
          import?{?SOME_CONSTANTS?}?from?"@/utils/constants";

          import?type?{?Foo?}?from?"@/typings/foo";
          import?type?{?Shared?}?from?"@/typings/shared";

          import?styles?from?"./index.module.scss";

          restrict-template-expressions

          模板字符串中的計(jì)算表達(dá)式其返回值必須是字符串,此規(guī)則可以被配置為允許數(shù)字、布爾值、可能為 null 的值以及正則表達(dá)式,或者你也可以允許任意的值,但這樣就沒意思了...

          為什么:在模板表達(dá)式中非字符串與數(shù)字以外的值很容易帶來潛在的問題,如:

          const?arr?=?[1,?2,?3];
          const?obj?=?{?name:?"linbudu"?};

          //?'arr:?1,2,3'
          const?str1?=?`arr:?${arr}`;
          //?'obj:?[object?Object]'
          const?str2?=?`obj:?${obj}`;

          無論哪種情況都不會(huì)是你想看到的,因?yàn)檫@實(shí)際上已經(jīng)脫離了你的掌控。推薦在規(guī)則配置中僅開啟 allowNumber 來允許數(shù)字,而禁止掉其他的類型,你所需要做得應(yīng)當(dāng)是在把這個(gè)變量填入模板字符串中時(shí)進(jìn)行一次具有實(shí)際邏輯的轉(zhuǎn)化。

          switch-exhaustiveness-check

          switch 的判定條件為 聯(lián)合類型 時(shí),其每一個(gè)類型分支都需要被處理。如:

          type?PossibleTypes?=?"linbudu"?|?"qiongxin"?|?"developer";

          let?value:?PossibleTypes;
          let?result?=?0;

          switch?(value)?{
          ??case?"linbudu":?{
          ????result?=?1;
          ????break;
          ??}
          ??case?"qiongxin":?{
          ????result?=?2;
          ????break;
          ??}
          ??case?"developer":?{
          ????result?=?3;
          ????break;
          ??}
          }

          為什么:工程項(xiàng)目中經(jīng)常出現(xiàn)的,導(dǎo)致問題發(fā)生的原因就是有部分功能邏輯點(diǎn)僅通過口口相傳,只看代碼你完全不知道自己還漏了什么地方。如聯(lián)合類型變量中每一條類型分支可能都需要特殊的處理邏輯。

          你也可以通過 TypeScript 中的 never 類型來實(shí)現(xiàn)實(shí)際代碼的檢驗(yàn):

          const?strOrNumOrBool:?string?|?number?|?boolean?=?false;

          if?(typeof?strOrNumOrBool?===?"string")?{
          ??console.log("str!");
          }?else?if?(typeof?strOrNumOrBool?===?"number")?{
          ??console.log("num!");
          }?else?if?(typeof?strOrNumOrBool?===?"boolean")?{
          ??console.log("bool!");
          }?else?{
          ??const?_exhaustiveCheck:?never?=?strOrNumOrBool;
          ??throw?new?Error(`Unknown?input?type:?${_exhaustiveCheck}`);
          }

          這里通過編譯時(shí)與運(yùn)行時(shí)做了兩重保障,確保為聯(lián)合類型新增類型分支時(shí)也需要被妥善的處理,你可以參考開頭的 never 類型 文章了解更多 never 相關(guān)的使用。除了聯(lián)合類型以外,你還可以通過 never 類型來確保每一個(gè)枚舉成員都需要處理。

          enum?PossibleType?{
          ??Foo?=?"Foo",
          ??Bar?=?"Bar",
          ??Baz?=?"Baz",
          }

          function?checker(input:?PossibleType)?{
          ??switch?(input)?{
          ????case?PossibleType.Foo:
          ??????console.log("foo!");
          ??????break;
          ????case?PossibleType.Bar:
          ??????console.log("bar!");
          ??????break;
          ????case?PossibleType.Baz:
          ??????console.log("baz!");
          ??????break;
          ????default:
          ??????const?_exhaustiveCheck:?never?=?input;
          ??????break;
          ??}
          }

          以上就是我們目前在使用的部分規(guī)則,還有一批規(guī)則或是涉及到高度的定制或是適用場(chǎng)景狹窄,這里就不做列舉了。如果你有什么想法,歡迎與我一起交流,但請(qǐng)注意:我不是在灌輸你一定要使用什么規(guī)則,我只是在分享我們使用的規(guī)則以及考量,因此在留言前請(qǐng)確認(rèn)不要屬于此類觀點(diǎn),感謝你的閱讀。

          參考資料

          [1]

          QCon+ 專題:TypeScript 在中大型項(xiàng)目中的落地實(shí)踐: https://qconplus.infoq.cn/2021/beijing2nth/track/1240

          [2]

          淘寶店鋪 TypeScript 研發(fā)規(guī)約落地: https://qconplus.infoq.cn/2021/beijing2nth/presentation/4161

          [3]

          proposal-error-cause: https://github.com/tc39/proposal-error-cause

          [4]

          strictFunctionTypes: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#strict-function-types


          最后,歡迎加入?魚皮的編程知識(shí)星球(點(diǎn)擊了解詳情),和 8200 多名小伙伴們一起交流學(xué)習(xí),向魚皮和大廠同學(xué) 1 對(duì) 1 提問、幫你制定學(xué)習(xí)計(jì)劃不迷茫、跟著魚皮直播做項(xiàng)目(往期項(xiàng)目可無限回看)領(lǐng)取魚皮原創(chuàng)編程學(xué)習(xí)/求職資料等。

          往期推薦

          幾個(gè)對(duì)程序員的誤解,害人不淺!

          編程導(dǎo)航,火了!

          Gitee 很無奈!

          幾行代碼,竟然就能做個(gè)聊天室!

          我造了個(gè)輪子,完整開源!

          瀏覽 128
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰人人摸人人操 | 性少妇暴力猛交69HD | 骚逼美女网站 | 天天操天天操天天操 | 久久黄色网址 |