使用泛型+type
定義類型函數
使用type
+泛型,可以定義類型函數:
type foo<T> = T;
type bar = foo<string>
可以對泛型入參進行約束、設置默認值
// 對入參進行約束
type foo<T extends string> = T;
type bar = foo<string>
// 設定默認值
type foo<T extends string = '123'> = T;
type bar = foo<string>
條件判斷
TypeScript中使用extends
進行類型判斷,與三元運算符很相似:
T extends U ? X : Y;
如果T
的類型能夠extends U
,結果返回X
,否則返回Y
。
結合上面的類型函數,可以進行擴展:
type num = 1;
type str = 'hello world';
type IsNumber<N> = N extends number ? 'yes is a number' : 'no not a number';
type result1 = IsNumber<num>; // 類型爲 'yes is a number'
type result2 = IsNumber<str> // 類型爲 'no not a number'
遍歷聯合類型
使用in
關鍵在來遍歷type
的聯合類型
聯合類型就是使用
|
來定義的類型的合集
type Key = 'vue' | 'react';
type Mapped = {[k in Key]: string}; // {vue: string; react: string}
const bar: Mapped = {
vue: 's',
react: 's'
}
如果聯合類型不是我們顯式的定義出來的,那麼想要動態的推導出聯合類型的類型,需要使用keyof
方法
interface Person {
name: string;
age: number;
}
type Foo = keyof Person; // 'name' | 'age'
對聯合類型進行map
操作
可以使用extends
+泛型,實現將一組聯合類型批量映射爲另一種類型:
type Foo = 'a' | 'b';
type UnionTypesMap<T, U> = U;
type Bar = UnionTypesMap<Foo, 5>
全局作用域
使用declare
關鍵字來聲明全局作用域:
declare module '*.png';
declare module '*.svg';
declare module '*.jpg';
declare type str = string;
declare interface Foo {
propA: string;
propB: number;
}
要注意,如果模塊使用了export
關鍵字導出了內容,上述方式可能會失效,需要顯式的聲明到全局:
declare global {
const ModuleGlobalFoo: string;
}
注意,上述方式之恩能夠用在模塊聲明內,也就是說代碼中必須包含export
,否則就會報錯:
TS2669: Augmentations for the global scope can only be directly nested in external modules or ambient module declarations.
模塊作用域
模塊作用域的觸發條件之一就是使用export
關鍵字導出內容,在其他模塊獲取時需要import
導入
never
類型的作用
never
類型代表空集,常用語校驗“類型收窄”是否符合預期,常被用來做“類型收底”。
例如,有一個聯合類型時:
interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
type All = Foo | Bar
在switch
判斷type
,TS是可以收窄類型的(discriminated union):
funcfunction handleValue(val: All) {
switch (val.type) {
case 'foo':
// 這裏 val 被收窄爲 Foo
break
case 'bar':
// val 在這裏是 Bar
break
default:
// val 在這裏是 never
const exhaustiveCheck: never = val
break
}
}
在default
裏面把收窄的never
的val
賦給了顯示聲明爲never
的變量。如果有一天type
類型被修改了:
type All = Foo | Bar | Baz
如果沒有在handleValue
添加針對Baz
的處理邏輯,這時候在default
的分支中就會編譯錯誤。所以通過這個辦法可以確保switch
總是窮盡了所有All
的可能性
用never
進行類型過濾聯合類型
當never
參與運算時T | never
的結果是T
,根據這個規則就可以過濾聯合類型中不符合期望的類型,TS內置的Exclude
泛型操作符就是根據這個原理實現的:
/**
* Exclude from T those types that are assignable to U
*/
type Exclude<T, U> = T extends U ? never : T;
type A = 1 | 2;
type B = Exclude<A, 1>; // 2
自定義類型守衛
類型守衛(Type Guard)的目的就是爲了對類型進行分流,將聯合類型分發到不同管道。可以出發類型守衛的常見方式有:typeof
、instancof
、in
、==
、===
、!=
、!==
等
function foo(x: A | B) {
if (x instanceof A) {
// x is A
} else {
// x is B
}
}
當以上的方式不滿足需求時,可以通過is
關鍵字自定義類型守衛:
function isA(x): x is number {
return true
}
function foo(x: unknown) {
if(isA(x)) {
return x
}
return null;
}