# 条件类型

类型也可以像 js 程序一样使用条件判断

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

type Example1 = Dog extends Animal ? number : string;
// type Example1 = number;

当做类型,这个很好用,可以根据入参的类型直接确定出参的类型,可以避免在使用联合类型时,必须使用类型断言。

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;

# 约束

使用约束来做类型检测

type MessageOf<T extends { message: unknown }> = T["message"];

interface Email {
  message: string;
}

type EmailMessageContents = MessageOf<Email>;
// type EmailMessageContents = string

使用约束做类型条件判断

type MessageOf<T> = 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 = never;

一个将数组类型扁平化的例子, 利用条件判断当前类型是不是数组,如果是数组,则返回其元素类型,如果不是,则原样返回。

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

// Extracts out the element type.
type Str = Flatten<string[]>;

// type Str = string;

// Leaves the type alone.
type Num = Flatten<number>;

// type Num = number;

# 在条件类型内推断

条件类型为我们提供了一种使用 infer 关键字从我们在 true 时的类型进行推断的方法。例如上面的 Flatten 实例。

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

这里,我们使用 infer 关键字来声明地引入一个名为 Item 的新泛型类型变量, 而不需要通过索引访问 T 的元素类型。这样就方便了很多

可以从函数类型中提取返回类型(后面会讲到专门的 ReturnType):

type GetReturnType<Type> = Type extends (...args: never[]) => infer Return
  ? Return
  : never;

type Num = GetReturnType<() => number>;

// type Num = number;

type Str = GetReturnType<(x: string) => string>;

// type Str = string;

type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;

// type Bools = boolean[];

注意约束 (...args: never[]) => infer Return ,差点看岔劈了

# 分配条件类型

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

type StrArrOrNumArr = ToArray<string | number>;

// type StrArrOrNumArr = string[] | number[]

万万没想到,我以为的结果是这样的 type StrArrOrNumArr = (string | number)[], 这样挺不错的,相当于 ToArray<string> | ToArray<number>;

如果将联合类型插入到 ToArray 中,则条件类型将应用于该联合的每个成员, 如果要实现不分配,要这样做

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

// 'StrArrOrNumArr' is no longer a union.
type StrArrOrNumArr = ToArrayNonDist<string | number>;

// type StrArrOrNumArr = (string | number)[];