<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>

          【TS】10 多個(gè) TypeScript 高級(jí)用法總結(jié)

          共 10757字,需瀏覽 22分鐘

           ·

          2021-10-17 23:36

          本文主要介紹 TypeScript 的高級(jí)用法,適用于對(duì) TypeScript 已經(jīng)有所了解或者已經(jīng)實(shí)際用過一段時(shí)間的同學(xué),分別從類型、運(yùn)算符、操作符、泛型的角度來系統(tǒng)介紹常見的 TypeScript 文章沒有好好講解的功能點(diǎn),最后再分享一下自己的實(shí)踐經(jīng)歷。

          一、 類型

          unknown

          unknown 指的是不可預(yù)先定義的類型,在很多場(chǎng)景下,它可以替代 any 的功能同時(shí)保留靜態(tài)檢查的能力。

          const?num:?number?=?10;
          (num?as?unknown?as?string).split('');???//?注意,這里和any一樣完全可以通過靜態(tài)檢查

          這個(gè)時(shí)候 unknown 的作用就跟 any 高度類似了,你可以把它轉(zhuǎn)化成任何類型,不同的地方是,在靜態(tài)編譯的時(shí)候,unknown 不能調(diào)用任何方法,而 any 可以。

          const?foo:?unknown?=?'string';
          foo.substr(1);????//?Error:?靜態(tài)檢查不通過報(bào)錯(cuò)
          const?bar:?any?=?10;
          any.substr(1);??//?Pass:?any類型相當(dāng)于放棄了靜態(tài)檢查

          unknown 的一個(gè)使用場(chǎng)景是,避免使用 any 作為函數(shù)的參數(shù)類型而導(dǎo)致的靜態(tài)類型檢查 bug:

          function?test(input:?unknown):?number?{
          ??if?(Array.isArray(input))?{
          ????return?input.length;????//?Pass:?這個(gè)代碼塊中,類型守衛(wèi)已經(jīng)將input識(shí)別為array類型
          ??}
          ??return?input.length;??????// Error:?這里的input還是unknown類型,靜態(tài)檢查報(bào)錯(cuò)。如果入?yún)⑹莂ny,則會(huì)放棄檢查直接成功,帶來報(bào)錯(cuò)風(fēng)險(xiǎn)
          }

          void

          在 TS 中,void 和 undefined 功能高度類似,可以在邏輯上避免不小心使用了空指針導(dǎo)致的錯(cuò)誤。

          function?foo()?{}???//?這個(gè)空函數(shù)沒有返回任何值,返回類型缺省為void
          const?a?=?foo();?//?此時(shí)a的類型定義為void,你也不能調(diào)用a的任何屬性方法

          void 和 undefined 類型最大的區(qū)別是,你可以理解為 undefined 是 void 的一個(gè)子集,當(dāng)你對(duì)函數(shù)返回值并不在意時(shí),使用 void 而不是 undefined。舉一個(gè) React 中的實(shí)際的例子。

          //?Parent.tsx
          function?Parent():?JSX.Element?{
          ??const?getValue?=?():?number?=>?{?return?2?};????/*?這里函數(shù)返回的是number類型?*/
          ??//?const?getValue?=?():?string?=>?{?return?'str'?};?/*?這里函數(shù)返回的string類型,同樣可以傳給子屬性?*/
          ??return?
          }
          //?Child.tsx
          type?Props?=?{
          ??getValue:?()?=>?void;??//?這里的void表示邏輯上不關(guān)注具體的返回值類型,number、string、undefined等都可以
          }
          function?Child({?getValue?}:?Props)?=>?<div>{getValue()}</div>

          never

          never 是指沒法正常結(jié)束返回的類型,一個(gè)必定會(huì)報(bào)錯(cuò)或者死循環(huán)的函數(shù)會(huì)返回這樣的類型。

          function?foo():?never?{?throw?new?Error('error?message')?}??//?throw?error?返回值是never
          function?foo():?never?{?while(true){}?}??//?這個(gè)死循環(huán)的也會(huì)無法正常退出
          function?foo():?never?{?let?count?=?1;?while(count){?count?++;?}?}??//?Error:?這個(gè)無法將返回值定義為never,因?yàn)闊o法在靜態(tài)編譯階段直接識(shí)別出

          還有就是永遠(yuǎn)沒有相交的類型。

          type?human?=?'boy'?&?'girl'?//?這兩個(gè)單獨(dú)的字符串類型并不可能相交,故human為never類型

          不過任何類型聯(lián)合上 never 類型,還是原來的類型。

          type?language?=?'ts'?|?never???//?language的類型還是'ts'類型

          關(guān)于 never 有如下特性:

          • 在一個(gè)函數(shù)中調(diào)用了返回 never 的函數(shù)后,之后的代碼都會(huì)變成deadcode
          function?test()?{
          ??foo();????//?這里的foo指上面返回never的函數(shù)
          ??console.log(111);??//?Error:?編譯器報(bào)錯(cuò),此行代碼永遠(yuǎn)不會(huì)執(zhí)行到
          }
          • 無法把其他類型賦給 never。
          let?n:?never;
          let?o:?any?=?{};
          n?=?o;??//?Error:?不能把一個(gè)非never類型賦值給never類型,包括any

          關(guān)于 never 的這個(gè)特性有一些很 hack 的用法和討論,比如這個(gè)知乎下的尤雨溪的回答:https://www.zhihu.com/question/354601204/answer/888551021。

          二、運(yùn)算符

          非空斷言運(yùn)算符 !

          這個(gè)運(yùn)算符可以用在變量名或者函數(shù)名之后,用來強(qiáng)調(diào)對(duì)應(yīng)的元素是非 null|undefined 的。

          function?onClick(callback?:?()?=>?void)?{
          ??callback!();??//?參數(shù)是可選入?yún)ⅲ恿诉@個(gè)感嘆號(hào)!之后,TS編譯不報(bào)錯(cuò)
          }

          你可以查看編譯后的 ES5 代碼,居然沒有做任何防空判斷。

          function?onClick(callback)?{
          ??callback();
          }

          這個(gè)符號(hào)的場(chǎng)景,特別適用于我們已經(jīng)明確知道不會(huì)返回空值的場(chǎng)景,從而減少冗余的代碼判斷,如 React 的 Ref。

          function?Demo():?JSX.Elememt?{
          ??const?divRef?=?useRef();
          ??useEffect(()?=>?{
          ????divRef.current!.scrollIntoView();??//?當(dāng)組件Mount后才會(huì)觸發(fā)useEffect,故current一定是有值的
          ??},?[]);
          ??return?Demo</div>
          }

          可選鏈運(yùn)算符 ?.

          相比上面!作用于編譯階段的非空判斷,?.這個(gè)是開發(fā)者最需要的運(yùn)行時(shí)(當(dāng)然編譯時(shí)也有效)的非空判斷。

          obj?.prop????obj?.[index]????func?.(args)

          ?.用來判斷左側(cè)的表達(dá)式是否是 null | undefined,如果是則會(huì)停止表達(dá)式運(yùn)行,可以減少我們大量的&&運(yùn)算。

          比如我們寫出a?.b時(shí),編譯器會(huì)自動(dòng)生成如下代碼

          a?===?null?||?a?===?void?0???void?0?:?a.b;

          這里涉及到一個(gè)小知識(shí)點(diǎn):undefined這個(gè)值在非嚴(yán)格模式下會(huì)被重新賦值,使用void 0必定返回真正的 undefined。

          空值合并運(yùn)算符 ??

          ??與||的功能是相似的,區(qū)別在于??在左側(cè)表達(dá)式結(jié)果為 null 或者 undefined 時(shí),才會(huì)返回右側(cè)表達(dá)式。

          比如我們書寫了let b = a ?? 10,生成的代碼如下:

          let?b?=?a?!==?null?&&?a?!==?void?0???a?:?10;

          而 || 表達(dá)式,大家知道的,則對(duì) false、''、NaN、0 等邏輯空值也會(huì)生效,不適于我們做對(duì)參數(shù)的合并。

          數(shù)字分隔符_

          let?num:number?=?1_2_345.6_78_9

          _可以用來對(duì)長(zhǎng)數(shù)字做任意的分隔,主要設(shè)計(jì)是為了便于數(shù)字的閱讀,編譯出來的代碼是沒有下劃線的,請(qǐng)放心食用。

          三、操作符

          鍵值獲取 keyof

          keyof 可以獲取一個(gè)類型所有鍵值,返回一個(gè)聯(lián)合類型,如下:

          type?Person?=?{
          ??name:?string;
          ??age:?number;
          }
          type?PersonKey?=?keyof?Person;??//?PersonKey得到的類型為?'name'?|?'age'

          keyof 的一個(gè)典型用途是限制訪問對(duì)象的 key 合法化,因?yàn)?any 做索引是不被接受的。

          function?getValue?(p:?Person,?k:?keyof?Person)?{
          ??return?p[k];??//?如果k不如此定義,則無法以p[k]的代碼格式通過編譯
          }

          總結(jié)起來 keyof 的語法格式如下:

          類型?=?keyof?類型

          實(shí)例類型獲取 typeof

          typeof 是獲取一個(gè)對(duì)象/實(shí)例的類型,如下:

          const?me:?Person?=?{?name:?'gzx',?age:?16?};
          type?P?=?typeof?me;??//?{?name:?string,?age:?number?|?undefined?}
          const?you:?typeof?me?=?{?name:?'mabaoguo',?age:?69?}??//?可以通過編譯

          typeof 只能用在具體的對(duì)象上,這與 js 中的 typeof 是一致的,并且它會(huì)根據(jù)左側(cè)值自動(dòng)決定應(yīng)該執(zhí)行哪種行為。

          const?typestr?=?typeof?me;???//?typestr的值為"object"

          typeof 可以和 keyof 一起使用(因?yàn)?typeof 是返回一個(gè)類型嘛),如下:

          type?PersonKey?=?keyof?typeof?me;???//?'name'?|?'age'

          總結(jié)起來 typeof 的語法格式如下:

          類型?=?typeof?實(shí)例對(duì)象

          遍歷屬性 in

          in 只能用在類型的定義中,可以對(duì)枚舉類型進(jìn)行遍歷,如下:

          //?這個(gè)類型可以將任何類型的鍵值轉(zhuǎn)化成number類型
          type?TypeToNumber?=?{
          ??[key?in?keyof?T]:?number
          }

          keyof返回泛型 T 的所有鍵枚舉類型,key是自定義的任何變量名,中間用in鏈接,外圍用[]包裹起來(這個(gè)是固定搭配),冒號(hào)右側(cè)number將所有的key定義為number類型。

          于是可以這樣使用了:

          const?obj:?TypeToNumber?=?{?name:?10,?age:?10?}

          總結(jié)起來 in 的語法格式如下:

          [?自定義變量名?in?枚舉類型?]:?類型

          四、泛型

          泛型在 TS 中可以說是一個(gè)非常重要的屬性,它承載了從靜態(tài)定義到動(dòng)態(tài)調(diào)用的橋梁,同時(shí)也是 TS 對(duì)自己類型定義的元編程。泛型可以說是 TS 類型工具的精髓所在,也是整個(gè) TS 最難學(xué)習(xí)的部分,這里專門分兩章總結(jié)一下。

          基本使用

          泛型可以用在普通類型定義,類定義、函數(shù)定義上,如下:

          //?普通類型定義
          type?Dog?=?{?name:?string,?type:?T?}
          //?普通類型使用
          const?dog:?Dog<number>?=?{?name:?'ww',?type:?20?}

          //?類定義
          class?Cat?{
          ??private?type:?T;
          ??constructor(type:?T)?{?this.type?=?type;?}
          }
          //?類使用
          const?cat:?Cat<number>?=?new?Cat<number>(20);?//?或簡(jiǎn)寫?const?cat?=?new?Cat(20)

          //?函數(shù)定義
          function?swipe<T,?U>(value:?[T,?U]):?[U,?T]?{
          ??return?[value[1],?value[0]];
          }
          //?函數(shù)使用
          swipenumber>,?Dog<number>>([cat,?dog])??//?或簡(jiǎn)寫?swipe([cat,?dog])

          注意,如果對(duì)一個(gè)類型名定義了泛型,那么使用此類型名的時(shí)候一定要把泛型類型也寫上去。

          而對(duì)于變量來說,它的類型可以在調(diào)用時(shí)推斷出來的話,就可以省略泛型書寫。

          泛型的語法格式簡(jiǎn)單總結(jié)如下:

          類型名<泛型列表>?具體類型定義

          泛型推導(dǎo)與默認(rèn)值

          上面提到了,我們可以簡(jiǎn)化對(duì)泛型類型定義的書寫,因?yàn)門S會(huì)自動(dòng)根據(jù)變量定義時(shí)的類型推導(dǎo)出變量類型,這一般是發(fā)生在函數(shù)調(diào)用的場(chǎng)合的。

          type?Dog?=?{?name:?string,?type:?T?}

          function?adopt(dog:?Dog)?{?return?dog?};

          const?dog?=?{?name:?'ww',?type:?'hsq'?};??//?這里按照Dog類型的定義一個(gè)type為string的對(duì)象
          adopt(dog);??//?Pass:?函數(shù)會(huì)根據(jù)入?yún)㈩愋屯茢喑?span style="box-sizing: border-box;color: rgb(230, 192, 123);line-height: 26px;">type為string

          若不適用函數(shù)泛型推導(dǎo),我們?nèi)粜枰x變量類型則必須指定泛型類型。

          const?dog:?Dog?=?{?name:?'ww',?type:?'hsq'?}??//?不可省略這部分

          如果我們想不指定,可以使用泛型默認(rèn)值的方案。

          type?Dog?=?{?name:?string,?type:?T?}
          const?dog:?Dog?=?{?name:?'ww',?type:?'hsq'?}
          dog.type?=?123;????//?不過這樣type類型就是any了,無法自動(dòng)推導(dǎo)出來,失去了泛型的意義

          泛型默認(rèn)值的語法格式簡(jiǎn)單總結(jié)如下:

          泛型名?=?默認(rèn)類型

          泛型約束

          有的時(shí)候,我們可以不用關(guān)注泛型具體的類型,如:

          function?fill<T>(length:?number,?value:?T):?T[]?{
          ??return?new?Array(length).fill(value);
          }

          這個(gè)函數(shù)接受一個(gè)長(zhǎng)度參數(shù)和默認(rèn)值,結(jié)果就是生成使用默認(rèn)值填充好對(duì)應(yīng)個(gè)數(shù)的數(shù)組。我們不用對(duì)傳入的參數(shù)做判斷,直接填充就行了,但是有時(shí)候,我們需要限定類型,這時(shí)候使用extends關(guān)鍵字即可。

          function?sum<T?extends?number>(value:?T[]):?number?{
          ??let?count?=?0;
          ??value.forEach(v?=>?count?+=?v);
          ??return?count;
          }

          這樣你就可以以sum([1,2,3])這種方式調(diào)用求和函數(shù),而像sum(['1', '2'])這種是無法通過編譯的。

          泛型約束也可以用在多個(gè)泛型參數(shù)的情況

          function?pick<T,?U?extends?keyof?T>(){};

          這里的意思是限制了 U 一定是 T 的 key 類型中的子集,這種用法常常出現(xiàn)在一些泛型工具庫中。

          extends 的語法格式簡(jiǎn)單總結(jié)如下,注意下面的類型既可以是一般意義上的類型也可以是泛型。

          泛型名?extends?類型

          泛型條件

          上面提到 extends,其實(shí)也可以當(dāng)做一個(gè)三元運(yùn)算符,如下:

          T?extends?U??X:?Y

          這里便不限制 T 一定要是 U 的子類型,如果是 U 子類型,則將 T 定義為 X 類型,否則定義為 Y 類型。

          注意,生成的結(jié)果是分配式的

          舉個(gè)例子,如果我們把 X 換成 T,如此形式:T extends U? T: never

          此時(shí)返回的 T,是滿足原來的 T 中包含 U 的部分,可以理解為 T 和 U 的交集。

          所以,extends 的語法格式可以擴(kuò)展為:

          泛型名A?extends?類型B???類型C:?類型D

          泛型推斷 infer

          infer 的中文是“推斷”的意思,一般是搭配上面的泛型條件語句使用的,所謂推斷,就是你不用預(yù)先指定在泛型列表中,在運(yùn)行時(shí)會(huì)自動(dòng)判斷,不過你得先預(yù)定義好整體的結(jié)構(gòu)。舉個(gè)例子:

          type?Foo?=?T?extends?{t:?infer?Test}???Test:?string

          首選看 extends 后面的內(nèi)容,{t: infer Test}可以看成是一個(gè)包含t屬性類型定義,這個(gè)t屬性的 value 類型通過infer進(jìn)行推斷后會(huì)賦值給Test類型,如果泛型實(shí)際參數(shù)符合{t: infer Test}的定義那么返回的就是Test類型,否則默認(rèn)給缺省的string類型。舉個(gè)例子加深下理解:

          type?One?=?Foo??//?string,因?yàn)閚umber不是一個(gè)包含t的對(duì)象類型
          type?Two?=?Foo<{t:?boolean}>??//?boolean,因?yàn)榉盒蛥?shù)匹配上了,使用了infer對(duì)應(yīng)的type
          type?Three?=?Foo<{a:?number,?t:?()?=>?void}>?//?()?=>?void,泛型定義是參數(shù)的子集,同樣適配

          infer用來對(duì)滿足的泛型類型進(jìn)行子類型的抽取,有很多高級(jí)的泛型工具也巧妙的使用了這個(gè)方法。

          五、泛型工具

          Partical

          此工具的作用就是將泛型中全部屬性變?yōu)榭蛇x的:

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

          舉個(gè)例子,這個(gè)類型定義在下面也會(huì)用到:

          type?Animal?=?{
          ??name:?string,
          ??category:?string,
          ??age:?number,
          ??eat:?()?=>?number
          }

          使用 Partical 包裹一下:

          type?PartOfAnimal?=?Partical;
          const?ww:?PartOfAnimal?=?{?name:?'ww'?};?//?屬性全部可選后,可以只賦值部分屬性了

          Record

          此工具的作用是將 K 中所有屬性值轉(zhuǎn)化為 T 類型,我們常用它來申明一個(gè)普通 object 對(duì)象。

          type?Recordextends?keyof?any,T>?=?{
          ??[key?in?K]:?T
          }

          這里特別說明一下,keyof any對(duì)應(yīng)的類型為number | string | symbol,也就是可以做對(duì)象鍵(專業(yè)說法叫索引 index)的類型集合。

          舉個(gè)例子:

          const?obj:?Record<string,?string>?=?{?'name':?'mbg',?'tag':?'年輕人不講武德'?}

          Pick

          此工具的作用是將 T 類型中的 K 鍵列表提取出來,生成新的子鍵值對(duì)類型。

          type?Pickextends?keyof?T>?=?{
          ??[P?in?K]:?T[P]
          }

          我們還是用上面的Animal定義,看一下 Pick 如何使用。

          const?bird:?Pick"name"?|?"age">?=?{?name:?'bird',?age:?1?}

          Exclude

          此工具是在 T 類型中,去除 T 類型和 U 類型的交集,返回剩余的部分。

          type?Exclude?=?T?extends?U???never?:?T

          注意這里的 extends 返回的 T 是原來的 T 中和 U 無交集的屬性,而任何屬性聯(lián)合 never 都是自身,具體可在上文查閱。

          舉個(gè)例子:

          type?T1?=?Exclude<"a"?|?"b"?|?"c",?"a"?|?"b">;???//?"c"
          type?T2?=?Exclude<string?|?number?|?(()?=>?void),?Function>;?//?string?|?number

          Omit

          此工具可認(rèn)為是適用于鍵值對(duì)對(duì)象的 Exclude,它會(huì)去除類型 T 中包含 K 的鍵值對(duì)。

          type?Omit?=?Pick>

          在定義中,第一步先從 T 的 key 中去掉與 K 重疊的 key,接著使用 Pick 把 T 類型和剩余的 key 組合起來即可。

          還是用上面的 Animal 舉個(gè)例子

          const?OmitAnimal:Omit'name'|'age'>?=?{?category:?'lion',?eat:?()?=>?{?console.log('eat')?}?}

          可以發(fā)現(xiàn),Omit 與 Pick 得到的結(jié)果完全相反,一個(gè)是取非結(jié)果,一個(gè)取交結(jié)果。

          ReturnType

          此工具就是獲取 T 類型(函數(shù))對(duì)應(yīng)的返回值類型。

          type?ReturnTypeextends?(...args:?any)?=>?any>
          ??=?T?extends?(...args:?any)?=>?infer?R???R?:?any;

          看源碼其實(shí)有點(diǎn)多,其實(shí)可以稍微簡(jiǎn)化成下面的樣子。

          type?ReturnTypeextends?func>?=?T?extends?()?=>?infer?R???R:?any;

          通過使用 infer 推斷返回值類型,然后返回此類型,如果你徹底理解了 infer 的含義,那這段就很好理解。

          舉個(gè)例子:

          function?foo(x:?string?|?number):?string?|?number?{?/*..*/?}
          type?FooType?=?ReturnType;??//?string?|?number

          Required

          此工具可以將類型 T 中所有的屬性變?yōu)楸剡x項(xiàng)。

          type?Required?=?{
          ??[P?in?keyof?T]-?:?T[P]
          }

          這里有一個(gè)很有意思的語法-?,你可以理解為就是 TS 中把?可選屬性減去的意思。

          除了這些以外,還有很多的內(nèi)置的類型工具,可以參考TypeScript Handbook[1]獲得更詳細(xì)的信息,同時(shí) Github 上也有很多第三方類型輔助工具,如utility-types[2]等。

          六、項(xiàng)目實(shí)戰(zhàn)

          這里分享一些我個(gè)人的想法,可能也許會(huì)比較片面甚至錯(cuò)誤,歡迎大家積極留言討論。

          Q: 偏好使用 interface 還是 type 來定義類型?

          A: 從用法上來說兩者本質(zhì)上沒有區(qū)別,大家使用 React 項(xiàng)目做業(yè)務(wù)開發(fā)的話,主要就是用來定義 Props 以及接口數(shù)據(jù)類型。

          但是從擴(kuò)展的角度來說,type 比 interface 更方便拓展一些,假如有以下兩個(gè)定義:

          type?Name?=?{?name:?string?};
          interface?IName?{?name:?string?};

          想要做類型的擴(kuò)展的話,type 只需要一個(gè)&,而 interface 要多寫不少代碼。

          type?Person?=?Name?&?{?age:?number?};
          interface?IPerson?extends?IName?{?age:?number?};

          另外 type 有一些 interface 做不到的事情,比如使用|進(jìn)行枚舉類型的組合,使用typeof獲取定義的類型等等。

          不過 interface 有一個(gè)比較強(qiáng)大的地方就是可以重復(fù)定義添加屬性,比如我們需要給window對(duì)象添加一個(gè)自定義的屬性或者方法,那么我們直接基于其 Interface 新增屬性就可以了。

          declare?global?{
          ????interface?Window?{?MyNamespace:?any;?}
          }

          總體來說,大家知道 TS 是類型兼容而不是類型名稱匹配的,所以一般不需用面向?qū)ο蟮膱?chǎng)景或者不需要修改全局類型的場(chǎng)合,我一般都是用 type 來定義類型。

          Q: 是否允許 any 類型的出現(xiàn)

          A: 說實(shí)話,剛開始使用 TS 的時(shí)候還是挺喜歡用 any 的,畢竟大家都是從 JS 過渡過來的,對(duì)這種影響效率的代碼開發(fā)方式并不能完全接受,因此不管是出于偷懶還是找不到合適定義的情況,使用 any 的情況都比較多。

          隨著使用時(shí)間的增加和對(duì) TS 學(xué)習(xí)理解的加深,逐步離不開了 TS 帶來的類型定義紅利,不希望代碼中出現(xiàn) any,所有類型都必須要一個(gè)一個(gè)找到對(duì)應(yīng)的定義,甚至已經(jīng)喪失了裸寫 JS 的勇氣。

          這是一個(gè)目前沒有正確答案的問題,總是要在效率和時(shí)間等等因素中找一個(gè)最適合自己的平衡。不過我還是推薦使用 TS,隨著前端工程化演進(jìn)和地位的提高,強(qiáng)類型語言一定是多人協(xié)作和代碼健壯最可靠的保障之一,多用 TS,少用 any,也是前端界的一個(gè)普遍共識(shí)。

          Q: 類型定義文件(.d.ts)如何放置

          A: 這個(gè)好像業(yè)界也沒有特別統(tǒng)一的規(guī)范,我的想法如下:

          • 臨時(shí)的類型,直接在使用時(shí)定義

          如自己寫了一個(gè)組件內(nèi)部的 Helper,函數(shù)的入?yún)⒑统鰠⒅还﹥?nèi)部使用也不存在復(fù)用的可能,可以直接在定義函數(shù)的時(shí)候就在后面定義。

          function?format(input:?{k:?string}[]):?number[]?{?/***/?}
          • 組件個(gè)性化類型,直接定義在 ts(x)文件中

          如 AntD 組件設(shè)計(jì),每個(gè)單獨(dú)組件的 Props、State 等專門定義了類型并 export 出去。

          //?Table.tsx
          export?type?TableProps?=?{?/***/?}
          export?type?ColumnProps?=?{?/***/?}
          export?default?function?Table()?{?/***/?}

          這樣使用者如果需要這些類型可以通過 import type 的方式引入來使用。

          • 范圍/全局?jǐn)?shù)據(jù),定義在.d.ts 文件中

          全局類型數(shù)據(jù),這個(gè)大家毫無異議,一般根目錄下有個(gè) typings 文件夾,里面會(huì)存放一些全局類型定義。

          假如我們使用了 css module,那么我們需要讓 TS 識(shí)別.less 文件(或者.scss)引入后是一個(gè)對(duì)象,可以如此定義:

          declare?module?'*.less'?{
          ??const?resource:?{?[key:?string]:?string?};
          ??export?=?resource;
          }

          而對(duì)于一些全局的數(shù)據(jù)類型,如后端返回的通用的數(shù)據(jù)類型,我也習(xí)慣將其放在 typings 文件夾下,使用 Namespace 的方式來避免名字沖突,如此可以節(jié)省組件 import 類型定義的語句。

          declare?namespace?EdgeApi?{
          ??interface?Department?{
          ????description:?string;
          ????gmt_create:?string;
          ????gmt_modify:?string;
          ????id:?number;
          ????name:?string;
          ??}
          }

          這樣,每次使用的時(shí)候,只需要const department: EdgeApi.Department即可,節(jié)省了不少導(dǎo)入的精力。開發(fā)者只要能約定規(guī)范,避免命名沖突即可。

          關(guān)于 TS 用法的總結(jié)就結(jié)束到這里,感謝大家的觀看~

          參考資料

          [1]?TypeScript Handbook:?

          https://www.typescriptlang.org/docs/handbook/utility-types.html


          [2]?utility-types:?

          https://github.com/piotrwitek/utility-types

          瀏覽 66
          點(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>
                  清纯唯美亚洲第一页麻豆豆花 | 操屄国产 | 久操久操久操 | 中文字幕一区二区久久人妻网站 | 国产9在线观看黄A片免费 |