【TypeScript】条件类型详解

TypeScript 是 JavaScript 的超集,提供了静态类型检查和其他特性,以提高代码的可读性和可维护性。其中,条件类型是一个强大的功能,可以根据输入的类型动态返回不同的类型。本文将详细探讨 TypeScript 中的条件类型,包括其语法、用法及实际应用场景。

一、条件类型概述

1. 什么是条件类型?

条件类型的语法类似于 JavaScript 的条件表达式,采用如下形式:

SomeType extends OtherType ? TrueType : FalseType;

SomeType 可以赋值给 OtherType 时,条件类型将返回 TrueType,否则返回 FalseType。条件类型能够根据类型之间的关系,灵活地推导出所需的类型。

2. 基本示例

让我们通过几个简单的示例来理解条件类型的工作原理:

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

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

在上面的示例中,Example1 返回 number,因为 Dog 确实扩展自 Animal;而 Example2 返回 string,因为 RegExp 并不扩展自 Animal

二、条件类型的实际应用

条件类型的真正威力在于与泛型结合使用。通过条件类型,我们可以编写更简洁、可复用的代码。以下是一个使用条件类型的函数示例:

1. 创建标签的函数

interface IdLabel {
    
    
  id: number;
}
interface NameLabel {
    
    
  name: string;
}

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

上面的函数使用了重载来处理不同类型的输入。虽然这种方式可以工作,但当可处理的类型增多时,重载数量会迅速增加。

2. 简化重载

我们可以使用条件类型来简化这个函数的重载:

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

function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
    
    
  throw "unimplemented";
}

在这个例子中,我们使用了条件类型 NameOrId,根据输入类型 T 的不同返回不同的类型。这样,我们就可以在一个函数中处理不同的输入类型,而无需创建多个重载。

扫描二维码关注公众号,回复: 17423153 查看本文章

3. 条件类型约束

条件类型不仅可以用于返回不同的类型,还可以用于约束泛型,提供更多的类型信息。例如:

type MessageOf<T> = T["message"];  // 此时会报错,因为 T 不确定是否有 message 属性

为了解决这个问题,我们可以添加一个约束:

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

这样,TypeScript 就能够正确推导出 T 中的 message 属性。

4. 处理缺少属性的情况

如果我们希望处理任意类型并在没有 message 属性时返回 never,我们可以进一步扩展:

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

这样,我们在使用时可以处理缺失 message 属性的情况:

interface Email {
    
    
  message: string;
}
interface Dog {
    
    
  bark(): void;
}

type EmailMessageContents = MessageOf<Email>;  // EmailMessageContents 为 string
type DogMessageContents = MessageOf<Dog>;  // DogMessageContents 为 never

三、推导与推断

1. 使用推断提取类型

条件类型允许我们使用 infer 关键字从类型中推断出子类型。例如,我们可以编写一个类型 Flatten 来扁平化数组类型:

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

在这里,我们使用 infer 关键字推导出 Type 的元素类型,而不是手动使用索引访问。这使得代码更加简洁和易于维护。

2. 提取函数的返回类型

我们还可以编写一个类型来提取函数的返回类型:

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

这样,我们就能轻松提取出不同函数的返回类型:

type Num = GetReturnType<() => number>;  // Num 为 number
type Str = GetReturnType<(x: string) => string>;  // Str 为 string

四、分发条件类型

1. 条件类型的分发行为

当条件类型作用于一个泛型类型时,它会对联合类型进行分发。来看一个例子:

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

type StrArrOrNumArr = ToArray<string | number>;  // StrArrOrNumArr 为 string[] | number[]

在这个例子中,ToArraystring | number 作为输入,分别对每个成员类型应用条件类型,返回 string[] | number[]

2. 避免分发行为

如果希望避免这种分发行为,可以使用方括号将 extends 关键字包裹起来:

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

type ArrOfStrOrNum = ToArrayNonDist<string | number>;  // ArrOfStrOrNum 为 (string | number)[]

五、总结

条件类型是 TypeScript 中一个非常强大的特性,它允许开发者根据输入类型动态推导输出类型,极大提高了代码的灵活性和可读性。通过结合泛型、推导和推断,条件类型能够帮助我们构建出更加健壮和可复用的代码。希望本文能够帮助你更好地理解和应用 TypeScript 中的条件类型,使你的开发工作更加高效!

推荐:


在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/lph159/article/details/143194031