typescript的never
类型表明永不存在的值的类型,它只能被赋值为never
。html
任意类型与never
交叉都获得never
:typescript
type T1 = number & never; // never
type T2 = string & never; // never
复制代码
能够这样理解:若type T = T1 & T2
,则T
类型的值能够赋给T1
或T2
类型的变量(相似类的继承关系)。 那么若与never
交叉,则T
类型的值能够赋给一个never
类型的变量,那T
只能是never
了。bash
任意类型与never
联合不受影响:ide
type T1 = number | never; // number
type T2 = string | never; // string
复制代码
理解与上述交叉类型状况相似: 若type T = T1 | T2
,则T1
或T2
类型的值能够赋给T
类型的变量。 因为never
类型能够赋给任意变量,天然对联合类型不产生影响了。ui
typescript的keyof
关键字,将一个类型映射为它全部成员名称的联合类型。如typescript官网上的示例:idea
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,如类型映射与索引类型,咱们得以在对象类型与联合类型之间游刃有余地转换,为工程中更多变量找到最适合的类型归属。spa
咱们看一个来自这里的例子:code
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中的那些字符串:htm
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";
复制代码
这样就达到了前文所述的目的。
咱们试图移除一个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
作一个索引类型,把对象类型变成联合类型,就获得了咱们想要的那些成员名称。