Hello~大家好。我是秋天的一阵风
🎉 欢迎来到 TypeScript 深度探索系列专栏!在这里,我们将深入探索 TypeScript 的各种奥秘,从基础到高级,一步步揭开它的神秘面纱。📚
🚀 本篇文章是该系列的一部分,专注于探究 TypeScript 的十二种工具类型。这些工具类型是 TypeScript 提供的强大工具,能够帮助你在开发中实现更灵活、更强大的类型操作。
🎉 通过本文的介绍,你将能够更好地利用 TypeScript 的工具类型,提升你的类型操作能力,编写出更健壮、更> 灵活的代码。让我们一起深入探索这些强大的工具类型吧!🔍
一、Required:让可选属性变得“必填”
Required
的作用是将一个类型中的所有属性都变为必选的。想象一下,你有一个接口,其中有些属性是可选的,但在某些情况下,你希望所有的属性都必须被明确地提供,这时候Required
就派上用场了。例如,我们有一个User
接口,其中email
属性是可选的:
interface User {
id: number;
name: string;
email?: string;
}
如果我们想要一个所有属性都必须提供的User
类型,就可以使用Required
:
type RequiredUser = Required<User>;
这样,RequiredUser
类型中的email
属性就从可选变成了必选。
使用场景
1. 确保用户输入的完整性
在处理用户输入时,可能需要确保用户提供了所有必要的信息。Required<T>
可以帮助你确保用户输入的完整性。
type UserInput = {
name?: string;
email?: string;
phone?: string;
};
function processInput(input: Required<UserInput>) {
console.log(input.name, input.email, input.phone);
}
// 处理用户输入时必须传入所有属性
processInput({ name: "John", email: "john@example.com", phone: "123-456-7890" });
// processInput({ name: "John" }); // Error: Property 'email' is missing in type '{ name: string; }' but required in type 'Required<UserInput>'.
2. 确保表单数据的完整性
在处理表单数据时,可能需要确保用户填写了所有必要的字段。Required<T>
可以帮助你确保表单数据的完整性。
type FormData = {
username?: string;
password?: string;
confirmPassword?: string;
};
function validateForm(data: Required<FormData>) {
if (data.password !== data.confirmPassword) {
throw new Error("Passwords do not match");
}
console.log(data.username, data.password);
}
// 调用时必须传入所有属性
validateForm({
username: "john_doe",
password: "123456",
confirmPassword: "123456",
});
// validateForm({ username: "john_doe" }); // Error: Property 'password' is missing in type '{ username: string; }' but required in type 'Required<FormData>'.
3. 确保 API 请求参数的完整性
在调用外部 API 时,通常需要确保请求参数是完整的。Required<T>
可以帮助你确保所有必要的参数都被传入。
type RequestOptions = {
method?: string;
url?: string;
headers?: { [key: string]: string };
body?: any;
};
function makeRequest(options: Required<RequestOptions>) {
console.log(options.method, options.url, options.headers, options.body);
}
// 调用时必须传入所有属性
makeRequest({
method: "GET",
url: "https://api.example.com/data",
headers: { "Content-Type": "application/json" },
body: null,
});
// makeRequest({ method: "GET" }); // Error: Property 'url' is missing in type '{ method: string; }' but required in type 'Required<RequestOptions>'.
Required的实现原理
Required
的实现原理与Partial
类似,但它将所有属性都标记为必选。它的定义如下:
type Required<T> = {
[P in keyof T]-?: T[P];
};
这里的关键是-?
,它表示移除属性的可选性,使得所有属性都必须被明确地提供。
二、Partial:让必选属性变得“可选”
与Required
相反,Partial
的作用是将一个类型中的所有属性都变为可选的。这在某些场景下非常有用,比如当你只需要部分属性时,或者在构建一个对象时,某些属性可能暂时没有值。继续使用上面的User
接口:
interface User {
id: number;
name: string;
email?: string;
}
type PartialUser = Partial<User>;
现在,PartialUser
类型中的id
、name
和email
属性都可以选择性地提供。
使用场景
1. 处理部分表单数据
在处理表单时,可能只需要部分字段,而不是整个表单数据。Partial<T>
可以帮助你定义这些部分字段。
type FormData = {
username: string;
password: string;
confirmPassword: string;
};
function updateForm(data: Partial<FormData>) {
console.log(data);
}
// 调用时可以只传入部分字段
updateForm({ username: "john_doe" });
updateForm({ password: "123456", confirmPassword: "123456" });
这样,我们在处理表单数据时,就可以灵活地处理用户输入的部分数据。
2. 处理部分 API 请求参数
在调用外部 API 时,可能只需要部分请求参数,而不是所有参数。Partial<T>
可以帮助你定义这些部分参数。
type RequestOptions = {
method: string;
url: string;
headers: { [key: string]: string };
body: any;
};
function makePartialRequest(options: Partial<RequestOptions>) {
console.log(options);
}
// 调用部分请求参数时可以只传入部分字段
makePartialRequest({ method: "GET", url: "https://api.example.com/data" });
makePartialRequest({ headers: { "Content-Type": "application/json" }, body: { key: "value" } });
Partial的实现原理
Partial
的实现原理基于TypeScript的映射类型。它通过遍历一个类型的所有属性,并将每个属性都标记为可选,来生成一个新的类型。具体来说,Partial
的定义如下:
type Partial<T> = {
[P in keyof T]?: T[P];
};
这里,keyof T
获取类型T
的所有属性名的联合类型,然后通过[P in keyof T]
遍历这些属性名,并在每个属性后面加上?
,表示该属性是可选的。
三、Exclude:从联合类型中排除特定类型
Exclude
的作用是从一个联合类型中排除特定的类型,生成一个新的联合类型。这在某些情况下非常有用,比如当你需要从一个联合类型中移除某些不希望出现的类型时,Exclude
就可以派上用场了。例如,我们有一个联合类型 Value
:
type Value = string | number | null | undefined;
如果我们需要一个不包含 null
和 undefined
的 Value
类型,就可以使用 Exclude
:
type NonNullableValue = Exclude<Value, null | undefined>;
这样,NonNullableValue
类型就只包含 string
和 number
。
使用场景
1. 过滤函数参数类型
在定义函数时,可能需要确保某些参数不包含特定的类型。Exclude<T, U>
可以帮助你实现这一点。
type Value = string | number | null | undefined;
function processValue(value: Exclude<Value, null | undefined>) {
console.log(value);
}
processValue("hello"); // "hello"
processValue(123); // 123
// processValue(null); // Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.
// processValue(undefined); // Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
2. 过滤数组元素类型
在处理数组时,可能需要确保数组中的某些元素不包含特定的类型。Exclude<T, U>
可以帮助你实现这一点。
type Value = string | number | null | undefined;
function filterValues(values: Value[]): Exclude<Value, null | undefined>[] {
return values.filter(value => value !== null && value !== undefined) as Exclude<Value, null | undefined>[];
}
const values: Value[] = ["hello", 123, null, undefined];
const filteredValues = filterValues(values);
console.log(filteredValues); // ["hello", 123]
3. 过滤事件处理函数的参数类型
在定义事件处理函数时,可能需要确保某些参数不包含特定的类型。Exclude<T, U>
可以帮助你实现这一点。
type EventData = { type: 'click'; x: number; y: number } | { type: 'mouseover'; clientX: number; clientY: number } | null;
function handleEvent(event: Exclude<EventData, null>) {
console.log(event.type, event.x, event.y);
}
const event: EventData = { type: 'click', x: 100, y: 200 };
handleEvent(event); // "click" 100 200
// handleEvent(null); // Error: Argument of type 'null' is not assignable to parameter of type '{ type: "click"; x: number; y: number; } | { type: "mouseover"; clientX: number; clientY: number; }'.
Exclude 的实现原理
Exclude
的实现原理是通过条件类型来排除特定的类型。它的定义如下:
type Exclude<T, U> = T extends U ? never : T;
这里的关键是 T extends U ? never : T
,它表示如果 T
是 U
的子类型,则返回 never
,否则返回 T
。这种方式使得 Exclude
能够从联合类型中移除特定的类型。
四、Extract:从联合类型中提取特定类型
Extract
的作用是从一个联合类型中提取特定的类型,生成一个新的联合类型。这在某些情况下非常有用,比如当你需要从一个联合类型中提取某些特定的类型时,Extract
就可以派上用场了。例如,我们有一个联合类型 Value
:
type Value = string | number | null | undefined;
如果我们需要一个只包含 string
和 number
的 Value
类型,就可以使用 Extract
:
type ExtractedValue = Extract<Value, string | number>;
这样,ExtractedValue
类型就只包含 string
和 number
。
使用场景
1. 提取特定类型的函数参数
在定义函数时,可能需要确保某些参数只包含特定的类型。Extract<T, U>
可以帮助你实现这一点。
type Value = string | number | null | undefined;
function processValue(value: Extract<Value, string | number>) {
console.log(value);
}
processValue("hello"); // "hello"
processValue(123); // 123
// processValue(null); // Error: Argument of type 'null' is not assignable to parameter of type 'string | number'.
// processValue(undefined); // Error: Argument of type 'undefined' is not assignable to parameter of type 'string | number'.
2. 提取特定类型的数组元素
在处理数组时,可能需要确保数组中的某些元素只包含特定的类型。Extract<T, U>
可以帮助你实现这一点。
type Value = string | number | null | undefined;
function filterValues(values: Value[]): Extract<Value, string | number>[] {
return values.filter(value => typeof value === 'string' || typeof value === 'number') as Extract<Value, string | number>[];
}
const values: Value[] = ["hello", 123, null, undefined];
const filteredValues = filterValues(values);
console.log(filteredValues); // ["hello", 123]
3. 提取特定类型的事件处理函数的参数
在定义事件处理函数时,可能需要确保某些参数只包含特定的类型。Extract<T, U>
可以帮助你实现这一点。
type EventData = { type: 'click'; x: number; y: number } | { type: 'mouseover'; clientX: number; clientY: number } | null;
function handleEvent(event: Extract<EventData, { type: 'click'; x: number; y: number }>) {
console.log(event.type, event.x, event.y);
}
const event: EventData = { type: 'click', x: 100, y: 200 };
handleEvent(event); // "click" 100 200
// handleEvent({ type: 'mouseover', clientX: 100, clientY: 200 }); // Error: Argument of type '{ type: "mouseover"; clientX: number; clientY: number; }' is not assignable to parameter of type '{ type: "click"; x: number; y: number; }'.
Extract 的实现原理
Extract
的实现原理是通过条件类型来提取特定的类型。它的定义如下:
type Extract<T, U> = T extends U ? T : never;
这里的关键是 T extends U ? T : never
,它表示如果 T
是 U
的子类型,则返回 T
,否则返回 never
。这种方式使得 Extract
能够从联合类型中提取特定的类型。
五、ReadOnly:让属性变得“只读”
ReadOnly
的作用是将一个类型中的所有属性都变为只读的。这意味着你不能修改这些属性的值,只能读取它们。这在某些情况下非常有用,比如当你希望某个对象在初始化后不能被修改时,ReadOnly
就可以派上用场了。例如,我们有一个 User
接口:
interface User {
id: number;
name: string;
email: string;
}
如果我们想要一个所有属性都只读的 User
类型,就可以使用 ReadOnly
:
type ReadOnlyUser = Readonly<User>;
使用场景
1. 确保配置对象不可变
在加载配置文件时,可能希望配置对象在初始化后不能被修改。Readonly<T>
可以帮助你实现这一点。
type ConfigFile = {
database: {
host: string;
port: number;
};
server: {
host: string;
port: number;
};
};
function loadConfig(config: Readonly<ConfigFile>) {
console.log(config.database.host, config.database.port, config.server.host, config.server.port);
}
// 加载配置文件时可以传入一个只读对象
const config: ConfigFile = {
database: { host: "localhost", port: 5432 },
server: { host: "localhost", port: 8080 },
};
loadConfig(config);
// config.database.host = "127.0.0.1"; // Error: Cannot assign to 'host' because it is a read-only property.
ReadOnly 的实现原理
Readonly
的实现原理是通过将所有属性标记为只读来实现的。它的定义如下:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
六、Record:创建属性键到值的映射
Record
的作用是创建一个类型,其属性键来自一个联合类型,而属性值来自另一个类型。这在某些情况下非常有用,比如当你需要创建一个对象,其键是某种特定的字符串或数字,而值是某种特定的类型时,Record
就可以派上用场了。例如,我们有一个联合类型 Keys
和一个值类型 Value
:
type Keys = 'a' | 'b' | 'c';
type Value = number;
type MyRecord = Record<Keys, Value>;
这样,MyRecord
类型就是一个对象,其键是 'a'
、'b'
或 'c'
,而值是 number
类型。
使用场景
1. 创建事件处理映射
在事件处理中,可能需要一个对象,其键是事件类型,而值是某种特定的事件处理函数。Record<K, T>
可以帮助你实现这一点。
type EventTypes = 'click' | 'mouseover' | 'mouseout';
type EventHandler = (event: Event) => void;
function createEventMap(events: Record<EventTypes, EventHandler>) {
console.log(events.click, events.mouseover, events.mouseout);
}
// 创建事件处理映射时可以传入一个对象,其键是 EventTypes 中的值,而值是 EventHandler 类型
createEventMap({
click: (event) => console.log('Clicked', event),
mouseover: (event) => console.log('Mouseover', event),
mouseout: (event) => console.log('Mouseout', event),
});
2. 创建国际化(i18n)映射
在国际化(i18n)中,可能需要一个对象,其键是语言代码,而值是某种特定的翻译对象。Record<K, T>
可以帮助你实现这一点。
type Locale = 'en' | 'fr' | 'es';
type Translation = { [key: string]: string };
function createTranslationMap(translations: Record<Locale, Translation>) {
console.log(translations.en, translations.fr, translations.es);
}
// 创建翻译映射时可以传入一个对象,其键是 Locale 中的值,而值是 Translation 类型
createTranslationMap({
en: { greeting: 'Hello', farewell: 'Goodbye' },
fr: { greeting: 'Bonjour', farewell: 'Au revoir' },
es: { greeting: 'Hola', farewell: 'Adiós' },
});
Record 的实现原理
Record
的实现原理是通过将一个联合类型中的每个值作为属性键,将另一个类型作为属性值来创建一个新的类型。它的定义如下:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
这里的关键是 [P in K]
,它表示遍历联合类型 K
中的每个值,并将其作为属性键,而 T
则是属性值的类型。
七、Pick:选择性地提取属性
Pick
的作用是从一个类型中选择性地提取一部分属性,生成一个新的类型。这在某些情况下非常有用,比如当你只需要部分属性时,Pick
就可以派上用场了。例如,我们有一个 User
接口:
interface User {
id: number;
name: string;
email: string;
age: number;
}
如果我们只需要 id
和 name
这两个属性,就可以使用 Pick
:
type PickUser = Pick<User, 'id' | 'name'>;
这样,PickUser
类型就只包含 id
和 name
这两个属性。
使用场景
1. 提取部分用户信息
在处理用户信息时,可能只需要部分字段,而不是整个用户对象。Pick<T, K>
可以帮助你提取这些部分字段。
type User = {
id: number;
name: string;
email: string;
age: number;
};
function getUserInfo(user: User): Pick<User, 'id' | 'name'> {
return { id: user.id, name: user.name };
}
const user: User = { id: 1, name: "John", email: "john@example.com", age: 30 };
const userInfo = getUserInfo(user);
console.log(userInfo); // { id: 1, name: "John" }
2. 提取部分表单数据
在处理表单数据时,可能只需要部分字段,而不是整个表单数据。Pick<T, K>
可以帮助你提取这些部分字段。
type FormData = {
username: string;
password: string;
confirmPassword: string;
};
function getFormUsername(data: FormData): Pick<FormData, 'username'> {
return { username: data.username };
}
const formData: FormData = { username: "john_doe", password: "123456", confirmPassword: "123456" };
const formUsername = getFormUsername(formData);
console.log(formUsername); // { username: "john_doe" }
Pick 的实现原理
Pick
的实现原理是通过指定一个联合类型中的键,从原类型中提取这些键对应的属性。它的定义如下:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
这里的关键是 [P in K]
,它表示遍历联合类型 K
中的每个键,并从类型 T
中提取这些键对应的属性。
八、Omit:排除特定属性
Omit
的作用是从一个类型中排除特定的属性,生成一个新的类型。这在某些情况下非常有用,比如当你需要排除某些不需要的属性时,Omit
就可以派上用场了。例如,我们有一个 User
接口:
interface User {
id: number;
name: string;
email: string;
age: number;
}
如果我们需要一个不包含 email
和 age
的 User
类型,就可以使用 Omit
:
type OmitUser = Omit<User, 'email' | 'age'>;
这样,OmitUser
类型就只包含 id
和 name
这两个属性。
使用场景
1. 排除敏感信息
在处理用户信息时,可能需要排除某些敏感信息,如 email
和 age
。Omit<T, K>
可以帮助你排除这些敏感信息。
type User = {
id: number;
name: string;
email: string;
age: number;
};
function getUserPublicInfo(user: User): Omit<User, 'email' | 'age'> {
return { id: user.id, name: user.name };
}
const user: User = { id: 1, name: "John", email: "john@example.com", age: 30 };
const userPublicInfo = getUserPublicInfo(user);
console.log(userPublicInfo); // { id: 1, name: "John" }
2. 排除不必要的表单字段
在处理表单数据时,可能需要排除某些不必要的字段,如 confirmPassword
。Omit<T, K>
可以帮助你排除这些字段。
type FormData = {
username: string;
password: string;
confirmPassword: string;
};
function getFormUsernamePassword(data: FormData): Omit<FormData, 'confirmPassword'> {
return { username: data.username, password: data.password };
}
const formData: FormData = { username: "john_doe", password: "123456", confirmPassword: "123456" };
const formUsernamePassword = getFormUsernamePassword(formData);
console.log(formUsernamePassword); // { username: "john_doe", password: "123456" }
Omit 的实现原理
Omit
的实现原理是通过排除指定的键,从原类型中生成一个新的类型。它的定义如下:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
这里的关键是 Exclude<keyof T, K>
,它表示从 keyof T
中排除 K
中的键,然后通过 Pick
提取剩余的键对应的属性。
九、NonNullable:排除 null
和 undefined
NonNullable
的作用是从一个类型中排除 null
和 undefined
,生成一个新的类型。这在某些情况下非常有用,比如当你需要确保某个值不是 null
或 undefined
时,NonNullable
就可以派上用场了。例如,我们有一个联合类型 Value
:
type Value = string | null | undefined;
如果我们需要一个不包含 null
和 undefined
的 Value
类型,就可以使用 NonNullable
:
type NonNullableValue = NonNullable<Value>;
这样,NonNullableValue
类型就只包含 string
。
使用场景
1. 确保函数参数不为 null
或 undefined
在定义函数时,可能需要确保某些参数不是 null
或 undefined
。NonNullable<T>
可以帮助你实现这一点。
type Value = string | null | undefined;
function processValue(value: NonNullable<Value>) {
console.log(value.toUpperCase());
}
processValue("hello"); // "HELLO"
// processValue(null); // Error: Argument of type 'null' is not assignable to parameter of type 'string'.
// processValue(undefined); // Error: Argument of type 'undefined' is not assignable to parameter of type 'string'.
2. 确保数组元素不为 null
或 undefined
在处理数组时,可能需要确保数组中的某些元素不是 null
或 undefined
。NonNullable<T>
可以帮助你实现这一点。
type Value = string | null | undefined;
function filterNonNullValues(values: Value[]): NonNullable<Value>[] {
return values.filter(value => value !== null && value !== undefined) as NonNullable<Value>[];
}
const values: Value[] = ["hello", null, "world", undefined];
const nonNullValues
NonNullable 的实现原理
NonNullable
的实现原理是通过 Exclude
来排除 null
和 undefined
。它的定义如下:
type NonNullable<T> = Exclude<T, null | undefined>;
这里的关键是 Exclude<T, null | undefined>
,它表示从类型 T
中排除 null
和 undefined
。这种方式使得 NonNullable
能够从联合类型中移除 null
和 undefined
,生成一个新的类型。
十、ReturnType:获取函数返回值类型
ReturnType
的作用是从一个函数类型中提取其返回值类型。这在某些情况下非常有用,比如当你需要对函数的返回值进行进一步处理或类型检查时,ReturnType
就可以派上用场了。例如,我们有一个函数 getUser
:
function getUser(): { id: number; name: string } {
return { id: 1, name: "John" };
}
如果我们需要获取 getUser
函数的返回值类型,就可以使用 ReturnType
:
type UserType = ReturnType<typeof getUser>;
这样,UserType
类型就是 { id: number; name: string }
。
使用场景
1. 对函数返回值进行进一步处理
在处理函数返回值时,可能需要对返回值进行进一步处理。ReturnType<T>
可以帮助你获取函数的返回值类型,从而进行更精确的类型检查。
function getUser(): { id: number; name: string } {
return { id: 1, name: "John" };
}
function processUser(user: ReturnType<typeof getUser>) {
console.log(user.id, user.name);
}
const user = getUser();
processUser(user); // 1 "John"
2. 确保函数返回值符合预期
在定义函数时,可能需要确保函数的返回值符合特定的类型。ReturnType<T>
可以帮助你进行类型检查。
function getNumber(): number {
return 42;
}
function getString(): string {
return "hello";
}
function processValue<T>(value: ReturnType<T>) {
console.log(value);
}
processValue(getNumber); // 42
processValue(getString); // "hello"
3. 对异步函数返回值进行处理
在处理异步函数返回值时,可能需要对返回值进行进一步处理。ReturnType<T>
可以帮助你获取异步函数的返回值类型,从而进行更精确的类型检查。
async function fetchData(): Promise<{ data: string }> {
return { data: "fetched data" };
}
async function processData(): Promise<string> {
const result = await fetchData();
return result.data;
}
function handleResult(result: ReturnType<typeof processData>) {
console.log(result);
}
processData().then(handleResult); // "fetched data"
ReturnType 的实现原理
ReturnType
的实现原理是通过条件类型来提取函数的返回值类型。它的定义如下:
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
这里的关键是 infer R
,它表示从函数类型 T
中推断返回值类型 R
。这种方式使得 ReturnType
能够从函数类型中提取返回值类型。
十一、Parameters:获取函数参数类型
Parameters
的作用是从一个函数类型中提取其参数类型。这在某些情况下非常有用,比如当你需要对函数的参数进行进一步处理或类型检查时,Parameters
就可以派上用场了。例如,我们有一个函数 add
:
function add(a: number, b: number): number {
return a + b;
}
如果我们需要获取 add
函数的参数类型,就可以使用 Parameters
:
type AddParameters = Parameters<typeof add>;
这样,AddParameters
类型就是 [number, number]
。
使用场景
1. 对函数参数进行进一步处理
在处理函数参数时,可能需要对参数进行进一步处理。Parameters<T>
可以帮助你获取函数的参数类型,从而进行更精确的类型检查。
function add(a: number, b: number): number {
return a + b;
}
function processAddParameters(params: Parameters<typeof add>) {
console.log(params[0], params[1]);
}
const params = [1, 2];
processAddParameters(params); // 1 2
2. 确保函数参数符合预期
在定义函数时,可能需要确保函数的参数符合特定的类型。Parameters<T>
可以帮助你进行类型检查。
function multiply(a: number, b: number): number {
return a * b;
}
function processMultiplyParameters(params: Parameters<typeof multiply>) {
console.log(params[0] * params[1]);
}
const params = [3, 4];
processMultiplyParameters(params); // 12
Parameters 的实现原理
Parameters
的实现原理是通过条件类型来提取函数的参数类型。它的定义如下:
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
这里的关键是 infer P
,它表示从函数类型 T
中推断参数类型 P
。这种方式使得 Parameters
能够从函数类型中提取参数类型。
十二、InstanceType:获取构造函数返回的实例类型
InstanceType
的作用是从一个构造函数类型中提取其返回的实例类型。这在某些情况下非常有用,比如当你需要对构造函数的实例进行进一步处理或类型检查时,InstanceType
就可以派上用场了。例如,我们有一个构造函数 User
:
class User {
constructor(public id: number, public name: string) {}
}
type UserInstance = InstanceType<typeof User>;
这样,UserInstance
类型就是 User
的实例类型。
使用场景
1. 对构造函数实例进行进一步处理
在处理构造函数实例时,可能需要对实例进行进一步处理。InstanceType<T>
可以帮助你获取构造函数的实例类型,从而进行更精确的类型检查。
class User {
constructor(public id: number, public name: string) {}
}
function processUserInstance(instance: InstanceType<typeof User>) {
console.log(instance.id, instance.name);
}
const user = new User(1, "John");
processUserInstance(user); // 1 "John"
2. 确保构造函数实例符合预期
在定义构造函数时,可能需要确保构造函数的实例符合特定的类型。InstanceType<T>
可以帮助你进行类型检查。
class Product {
constructor(public id: number, public name: string, public price: number) {}
}
function processProductInstance(instance: InstanceType<typeof Product>) {
console.log(instance.id, instance.name, instance.price);
}
const product = new Product(1, "Laptop", 1200);
processProductInstance(product); // 1 "Laptop" 1200
InstanceType 的实现原理
InstanceType
的实现原理是通过条件类型来提取构造函数的实例类型。它的定义如下:
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
这里的关键是 infer R
,它表示从构造函数类型 T
中推断实例类型 R
。这种方式使得 InstanceType
能够从构造函数类型中提取实例类型。