Appearance
构造函数与异步相关类型工具
构造函数相关类型工具
ConstructorParameters<Type>
ConstructorParameters<Type> 从构造函数类型中获取构造函数参数的元组类型。
基本用法
typescript
class User {
constructor(
public id: number,
public name: string,
) {}
}
type ConstructorParamsType1 = ConstructorParameters<typeof User>; // [id: number, name: string]实现原理
对于 ConstructorParameters 的实现其实和之前的 ReturnType 十分类似,只不过现在我们需要针对构造函数类型进行处理:
typescript
type MyConstructorParams<T extends abstract new (...args: any[]) => any> = T extends abstract new (...args: infer R) => any ? R : never;
type ConstructorParamsType2 = MyConstructorParams<typeof User>;应用场景
我们可以扩展一个场景实现一下效果:
typescript
class Book {
title: string;
content?: string;
constructor(title: string) {
this.title = title;
}
}
class CreateInstance<T extends new (...args: any[]) => any> {
private ClassConstructor: T;
constructor(classConstructor: T) {
this.ClassConstructor = classConstructor;
}
getInstance(...args: ConstructorParameters<T>): InstanceType<T> {
return new this.ClassConstructor(...args);
}
}
const instanceCreator = new CreateInstance(Book);
const book = instanceCreator.getInstance('Typescript类型全解');InstanceType<Type>
InstanceType<Type> 可以通过构造函数类型得到其实例对象类型。
基本用法
typescript
class User {
constructor(
public id: number,
public name: string,
) {}
}
type U = InstanceType<typeof User>;实现原理
要去实现这个类型工具也非常简单:
typescript
type MyInstanceType<T extends abstract new (...args: any[]) => any> = T extends abstract new (...args: any[]) => infer R ? R : never;
type InstanceUser = MyInstanceType<typeof User>;注意:
typeof User中的User代表的是类本身(构造函数),而不仅仅是类型。
深度探讨:TS 中可以直接写构造函数吗?
有这个思考,说明你对类型系统的认知还不够明确!
函数是具有二义性的,因此 ES6 中才有了箭头函数和类的区分。在 TS 中:
- 如果要使用类,应该使用
class关键字。 - 如果要使用函数,那么它就是一个普通函数。
我们可以接着这个“错误”思考,来深入认知一下类的类型。
typescript
function Person(id: number, name: string, age: number) {
this.id = id;
this.name = name;
this.age = age;
}首先,这种写法在 TS 中会直接报错。要尝试运行,至少需要将 noImplicitThis 设置为 false。但即使这样,这个在 JS 中认知的构造函数还不能直接 new:
typescript
const person = new Person(1, 'John Doe', 30); // Error: 其目标缺少构造签名的 "new" 表达式因为编译器不知道 new 出来的是什么。我们可以使用简单的断言来屏蔽错误:
typescript
const person = new (Person as any)(1, 'John Doe', 30);但这样得到的 person 是 any 类型,没有任何类型提示。更关键的是,使用 InstanceType 也是徒劳的:
typescript
type PersonInstance = InstanceType<typeof Person>; // Error: 类型不匹配原因:typeof Person 获取的仅仅是一个函数类型,而不是构造函数类型。
解决方案:人为构造类型
如果我们必须使用这种旧式写法,可以通过以下方式填补类型缺陷:
typescript
interface Person {
id: number;
name: string;
age: number;
}
type PersonConstructor = new (id: number, name: string, age: number) => Person;
// 或者使用接口形式定义构造签名:
// interface PersonConstructor {
// new(id: number, name: string, age: number): Person;
// readonly prototype: Person;
// }
type PersonInstance = InstanceType<PersonConstructor>;
const person: PersonInstance = new (Person as any)(1, 'John Doe', 30);异步相关类型工具:Awaited<Type>
Awaited<Type> 用于获取 Promise 中的类型(模拟 await 或 .then() 的解包行为)。
基本用法
typescript
type T = Awaited<Promise<string>>; // string应用场景:处理第三方接口
当从第三方库获取的函数返回 Promise 时,Awaited 可以帮我们提取出实际的数据类型。
typescript
interface User {
id: number;
firstName: string;
lastName: string;
age: number;
}
async function fetchUser(): Promise<User> {
const data = await fetch('https://api.example.com/user').then((res) => res.json());
return data;
}
// 提取 fetchUser 的返回值类型
type UserFetch = Awaited<ReturnType<typeof fetchUser>>;
const user: UserFetch = {
id: 1,
firstName: 'yuan',
lastName: 'jin',
age: 18,
};实现原理
Awaited 的内部实现利用了递归条件类型:
typescript
type MyAwaited<T> = T extends null | undefined
? T
: T extends object & { then(onfulfilled: infer F, ...args: infer _): any }
? F extends (value: infer V, ...args: infer _) => any
? MyAwaited<V> // 递归解包
: never
: T;递归与嵌套处理
Awaited 会递归解包,直到获取到非 Promise 类型:
typescript
type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<Promise<number>>>; // number
type C = Awaited<boolean | Promise<number>>; // number | boolean
type D = Awaited<number>; // number进阶:约束为 Promise 类型
如果希望传入的必须是 Promise 类型,否则报错,可以结合 PromiseLike 进行封装:
typescript
type StrictAwaited<T extends PromiseLike<any>> = Awaited<T>;
type F = StrictAwaited<number>; // Error: 类型“number”不满足约束“PromiseLike<any>”