学习呦
14 10月 2025 6 min read

条件类型 conditional types

Written by 米豆

可以通过条件表达式,根据条件表达式判断的结果返回一个类型

⚠️这里也用到了 extends关键字, extends...?:这个是条件表达式,不是一个东西

例子

interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}

type Example1 = Dog extends Animal ? number : string;
// type Example1 = number
// 条件表达式是:Dog extends Animal 接口Dog是否继承了接口Animal,是的话返回一个number类型,不是则返回string类型

条件类型可以简化一些写法,比如说

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";
}

有一个createLabel方法,接收id: number或者name: string类型,返回两个接口类型的数据IdLabel和NameLabel

上面用了穷举,把所有可能的的接收和返回都列出来了

但是可以用条件类型来简化一下

type NameOrId<T extends number | string> = T extends number
? IdLabel
: NameLabel;

infer关键字 推断关键字(只能在条件类型里面用)

这里有点绕

如何拿一个任意类型数组中一个元素的类型呢

type Flatten<T> = T extends any[] ? T[number] : T;

上面的做法当然可以,T为一个任意数组类型,任意的数组类型肯定会继承any[]类型,之后使用了索引获取类型(indexed access types)(也就是number,毕竟一个数组的索引都是数字嘛)获得这个T类型数组中元素的类型

这样做扩大了类型,没有做到类型约束

下面是做了类型约束的写法

type Flatten<T> = T extends Array<infer Item> ? Item : T;

infer Item就是推断出数组中每一个元素的类型,Item是每一个元素的类型

所以这个infer是作为一个类型变量的前关键字的,而不是值变量的关键字。比如说这里不能用typeof代替infer

回归正题,上面写的好处就是明确指定了T类型的数组是不是继承了Array<infer Item>类型数组,比如说

type Str = Flatten<string[]>

// type Str = string

这样做到了绝对的一对一,也就是类型约束

分发条件类型(在条件类型中使用联合类型)

例子

type ToArray<Type> = Type extends any ? Type[] : never;

type StrArrOrNumArr = ToArray<string | number>;

给了一个string | number类型,根据条件类型表达式走true条件,会返回string[] | number[]联合类型

传进去的联合类型string | number,和ToArray组装成ToArray<string> | ToArray<number>,再和数组类型组装成数组联合类型string[] | number[]

如果想要一个联合数组类型呢(string | number)[]

需要用方括号,把类型括起来

type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never;

type StrArrOrNumArr = ToArrayNonDist<string | number>;
↑