never型別
typescript的never
型別代表永不存在的值的型別,它只能被賦值為never
。
任意型別與never
交叉都得到never
:
type T1 = number & never; // never
type T2 = string & never; // never
複製程式碼
可以這樣理解:若type T = T1 & T2
,則T
型別的值可以賦給T1
或T2
型別的變數(類似類的繼承關係)。
那麼若與never
交叉,則T
型別的值可以賦給一個never
型別的變數,那T
只能是never
了。
任意型別與never
聯合不受影響:
type T1 = number | never; // number
type T2 = string | never; // string
複製程式碼
理解與上述交叉型別情況類似:
若type T = T1 | T2
,則T1
或T2
型別的值可以賦給T
型別的變數。
由於never
型別可以賦給任意變數,自然對聯合型別不產生影響了。
keyof
typescript的keyof
關鍵字,將一個型別對映為它所有成員名稱的聯合型別。如typescript官網上的示例:
interface Person {
name: string;
age: number;
location: string;
}
type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[]; // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person }; // string
複製程式碼
keyof實際給我們一個操作聯合型別的途徑。結合typescript的其他feature,如型別對映與索引型別,我們得以在物件型別與聯合型別之間遊刃有餘地轉換,為工程中更多變數找到最適合的型別歸屬。
應用
Diff Type
我們看一個來自這裡的例子:
type Diff<T extends string, U extends string> = ({ [P in T]: P } & { [P in U]: never })[T];
type Omit<T, K extends keyof T> = Pick<T, Diff<keyof T, K>>;
複製程式碼
下面一行不用過多解釋了,在T型別中除去成員名在K中的成員。而上面一行程式碼特別有意思,也特別難看懂,它所做的是對於T與U兩個字串字面量型別,從T中除去包含在U中的那些字串:
type A = Diff<"a" | "b" | "c", "a">; // "b" | "c"
type B = Diff<"a" | "b" | "c", "b" | "d">; // "a" | "c"
複製程式碼
它是如何做到的呢?我們首先看它的前面部分:
type FirstHalf<T extends string, U extends string> = { [P in T]: P } & { [P in U]: never }
type C = FirstHalf<"a" | "b" | "c", "b" | "d">;
// {
// "a": "a",
// "b": "b",
// "c": "c"
// } & {
// "b": never,
// "d": never
// }
複製程式碼
我們再將type C
做逐成員的交叉:
type C = {
"a": "a",
"b": "b" & never,
"c": "c",
"d": never
}
複製程式碼
任意型別與never
交叉的結果都是never
,因此
type C = {
"a": "a",
"b": never,
"c": "c",
"d": never
}
複製程式碼
我們再看Diff
型別:
type B = Diff<"a" | "b" | "c", "b" | "d">;
= {
"a": "a",
"b": never,
"c": "c",
"d": never
}["a" | "b" | "c"];
= "a" | never | "c";
= "a" | "c";
複製程式碼
這樣就達到了前文所述的目的。
去除所有never成員
我們試圖移除一個object type
中所有型別為never
的成員。可以這樣操作:
type OmitNever<T> = Pick<T, {[P in keyof T]: T[P] extends never ? never : P}[keyof T]>;
type T = {
a: string,
b: never,
c: string,
}
type T1 = OmitNever<T>; // { a: string, c: string }
複製程式碼
原理類似第一個例子。我們試圖把T
中所有非never
成員的名稱找出,從T
中pick
出來。所以先弄一個物件型別出來:
type OmitNeverHalf<T> = {[P in keyof T]: T[P] extends never ? never : P}
type TT = OmitNeverHalf<T>;
// {
// "a": "a",
// "b": never,
// "c": "c"
// }
複製程式碼
再用keyof T
做一個索引型別,把物件型別變成聯合型別,就得到了我們想要的那些成員名稱。