0%

条件类型

条件类型

基于输入的值的类型决定输出的值的类型,条件类型就是用来帮助我们描述输入类型和输出类型之间的关系

1
2
3
4
5
6
7
8
9
10
11
12
13
interface IdLabel {
id: number /* some fields */;
}
interface NameLabel {
name: string /* other fields */;
}

function createLabel(id: number): IdLabel;
function createLabel(name: string): NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel {
throw "unimplemented";
}

把逻辑写在条件类型中可以简化掉函数重载:

1
2
3
4
5
6
7
8
9
10
type NameOrId<T extends number|string>=T extends number?IdLabel:NameLabel;
function createLabel<T extends number|string>(idOrName:T):NameOrId<T>{
throw "unimplemented";
}
let a=createLabel("typescript");
//let a:NameLabel
let b=createLabel(2.8);
//let b:IdLabel
let c=createLabel(Math.radom()?"hello":42);
//let c:NameLabel|IdLabel

条件类型约束

1
2
3
4
5
6
7
8
9
10
11
type MessageOf<T extends {message:unknown} ? T["message"]:never;
interface Email {
message:string;
}
interface Dog {
bark():void;
}
type EmailMessageContents=MessageOf<Email>;
//type EmailMessageContents=string
type DogMessageContents=MessageOf<Dog>;
//type DogMessageContents=nerver

在条件类型里推断

infer关键词,可以从正在比较的类型中推断类型,然后在true分支里引用该推断结果

1
2
3
4
5
6
7
type Flatten<T>=T extends any[]?T[number]:T;//number索引用来获取数组元素的类型
type Str=Flatten<string[]>;
//type Str=string
type Num=Flatten<number>;
//type Num=number;
//用infer
type Flatten<Type>=Type extends Array<infer Item>?Item:Type

使用infer写一些有用的类型帮助别名,我们可以获取一个函数返回的类型:

1
2
3
4
5
type GetReturnType<Type>=Type extends (...args:nerver[])=>infer Return ?Return :never;
type Num=GetReturnType<()=>number>;
//type Num=number;
type Str=GetReturnType<(x:string)=>string>;
//type Str=string

当从多重调用签名(比如重载函数)中推断类型时,会按照最后的签名进行推断,因为一般这个签名是用来处理所有情况的签名

分发条件类型

在泛型中使用条件类型时,如果传入一个联合类型,就会变成分发的。

1
2
type ToArray<Type>=Type extends any?Type[]:nerver;

在ToArray传入一个联合类型,这个条件类型会被应用到联合类型的每个成员

1
2
type StrArrOrNumArr=ToArr<string|number>;
//type StrArrOrNumArr=string[]|number[]

先遍历联合类型string|number里的每一个成员,得到ToArray|ToArray,最后结果为string[]|number[]

如果要避免这种行为,用方括号包裹extends关键字的每一部分

1
2
3
4
type ToArrayNonDist<Type>=[Type] extends [any] ? Type[]: nerver;
type StrArrOrNumArr=ToArrayNonDist<string|number>;
//type StrArrOrNumArr = (string | number)[]

先得到ToArrayNonDist<string|number>结果为(string|number)[]

参考:

https://ts.yayujs.com/handbook/ConditionalTypes.html#%E5%88%86%E5%8F%91%E6%9D%A1%E4%BB%B6%E7%B1%BB%E5%9E%8B-distributive-conditional-types

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