返回介绍

类型别名中的泛型

发布于 2024-09-11 00:55:47 字数 3632 浏览 0 评论 0 收藏 0

类型别名如果声明了泛型坑位,它就等价于一个接受参数的函数:

type Factory<T> = T | number | string;

这个类型别名的本质就是一个函数,T 就是它的变量,返回值则是一个包含 T 的联合类型。

类型别名中的泛型大多是用来进行工具类型封装。

type Stringify<T> = {
  [K in keyof T]: string;
};

type Clone<T> = {
  [K in keyof T]: T[K];
};

Stringify 会将一个对象类型的所有属性类型置为 string ,而 Clone 则会进行类型的完全复制。

type Partial<T> = {
    [P in keyof T]?: T[P];
};

工具类型 Partial 会将传入的对象类型复制一份,但会额外添加一个?,它会将所有属性转变为可选属性。

interface IFoo {
  prop1: string;
  prop2: number;
  prop3: boolean;
  prop4: () => void;
}

type PartialIFoo = Partial<IFoo>;

// 等价于
interface PartialIFoo {
  prop1?: string;
  prop2?: number;
  prop3?: boolean;
  prop4?: () => void;
}

类型别名与泛型的结合中,还有一个非常重要的工具:条件类型。

type IsEqual<T> = T extends true ? 1 : 2;

type A = IsEqual<true>; // 1
type B = IsEqual<false>; // 2
type C = IsEqual<'wangxiaobai'>; // 2

在条件类型参与的情况下,通常泛型会被作为条件类型中的判断条件(T extends Condition,或者 Type extends T)以及返回值(即 : 两端的值),这也是筛选类型需要依赖的能力之一。

泛型约束与默认值

泛型同样有着默认值的设定。

type Factory<T = boolean> = T | number | string;

在你调用时可以不带任何参数,默认会使用声明的默认值来填充。

const foo: Factory = false;

泛型还能做到函数参数做不到的事:泛型约束,可以要求传入这个工具类型的泛型必须符合某些条件,否则就拒绝进行后面的逻辑。

使用 extends 关键字来约束传入的泛型参数必须符合要求。关于 extends,A extends B 意味着 A 是 B 的子类型,也就是说 A 比 B 的类型更精确,或者说更复杂。

  • 更精确:如字面量类型是对应原始类型的子类型,即 'wangxiaobai' extends string,18 extends number 成立。类似的,联合类型子集均为联合类型的子类型,即 1、 1 | 2 是 1 | 2 | 3 | 4 的子类型。
  • 更复杂,如 { name: string } 是 {} 的子类型,因为在 {} 的基础上增加了额外的类型,基类与派生类(父类与子类)同理。
type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002
  ? 'success'
  : 'failure';

这个例子会根据传入的请求码判断请求是否成功,这意味着它只能处理数字字面量类型的参数,因此这里通过 extends number 来标明其类型约束,如果传入一个不合法的值,就会出现类型错误:

type ResStatus<ResCode extends number> = ResCode extends 10000 | 10001 | 10002
  ? 'success'
  : 'failure';


type Res1 = ResStatus<10000>; // "success"
type Res2 = ResStatus<20000>; // "failure"

type Res3 = ResStatus<'10000'>; // 类型“string”不满足约束“number”。

如果想让这个类型别名可以无需显式传入泛型参数也能调用,并且默认情况下是成功地,这样就可以为这个泛型参数声明一个默认值:

type ResStatus<ResCode extends number = 10000> = ResCode extends 10000 | 10001 | 10002
  ? 'success'
  : 'failure';

type Res4 = ResStatus; // "success"

多泛型关联

不仅可以同时传入多个泛型参数,还可以让这几个泛型参数之间也存在联系。

type Conditional<Type, Condition, TruthyResult, FalsyResult> =
  Type extends Condition ? TruthyResult : FalsyResult;

//  "passed!"
type Result1 = Conditional<'wangxiaobai', string, 'passed!', 'rejected!'>;

// "rejected!"
type Result2 = Conditional<'wangxiaobai', boolean, 'passed!', 'rejected!'>;

多泛型参数其实就像接受更多参数的函数,其内部的运行逻辑(类型操作)会更加抽象,表现在参数(泛型参数)需要进行的逻辑运算(类型操作)会更加复杂。

多个泛型参数之间的依赖,其实指的即是在后续泛型参数中,使用前面的泛型参数作为约束或默认值:

type ProcessInput<
  Input,
  SecondInput extends Input = Input,
  ThirdInput extends Input = SecondInput
> = number;
  • 这个工具类型接受 1-3 个泛型参数。
  • 第二、三个泛型参数的类型需要是首个泛型参数的子类型。
  • 当只传入一个泛型参数时,其第二个泛型参数会被赋值为此参数,而第三个则会赋值为第二个泛型参数,相当于均使用了这唯一传入的泛型参数。
  • 当传入两个泛型参数时,第三个泛型参数会默认赋值为第二个泛型参数的值。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文