【TS】10 多個(gè) TypeScript 高級(jí)用法總結(jié)
本文主要介紹 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
