前言
TypeScript 的类型系统是其最强大的特性之一,它提供了丰富的内置工具类型(Utility Types),让我们能够以声明式的方式操作和转换类型。这些工具类型就像是类型系统的"瑞士军刀",能大幅提升前端同学的开发效率并减少错误。本文将带您了解 TypeScript 常用的内置工具类型,并基于它们构建强大的高阶TS类型扩展。✨✨✨
一、TypeScript 常用内置工具类型详解📝📝📝
1. 基本对象工具
Partial<T>
- 将一个类型的所有属性设置为可选
ts
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
/* 等价于:
{
id?: number;
name?: string;
email?: string;
}
*/
Required<T>
- 将一个类型的所有属性设置为必需
ts
type OptionalUser = {
id?: number;
name?: string;
};
type RequiredUser = Required<OptionalUser>;
/* 等价于:
{
id: number;
name: string;
}
*/
Readonly<T>
- 使一个类型的所有属性变为只读
ts
interface MutableConfig {
apiUrl: string;
timeout: number;
}
type ImmutableConfig = Readonly<MutableConfig>;
/* 等价于:
{
readonly apiUrl: string;
readonly timeout: number;
}
*/
const config: Readonly<Config> = { theme: "dark" };
// config.theme = "light" // 错误:只读属性
Record<K, T>
- 创建一个具有特定键类型和值类型的对象类型
创建一个具有特定键类型和值类型的对象类型。
ts
type PagePaths = Record<'home' | 'about' | 'contact', string>;
/* 等价于:
{
home: string;
about: string;
contact: string;
}
*/
type UserMap = Record<number, User>;
/* 等价于:
{
[key: number]: User;
}
*/
2. 属性选择工具
Pick<T, K>
- 从一个类型T挑选指定的属性K来创建新类型
ts
interface FullUser {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Pick<FullUser, 'id' | 'name' | 'email'>;
/* 等价于:
{
id: number;
name: string;
email: string;
}
*/
Omit<T, K>
- 从类型T排除指定的属性K来创建新类型
ts
interface FullUser {
id: number;
name: string;
email: string;
password: string;
}
type SecureUser = Omit<FullUser, 'password'>;
/* 等价于:
{
id: number;
name: string;
email: string;
}
*/
3. 联合类型工具
Exclude<T, U>
- 从类型T中排除可赋值给类型U的类型
ts
type T = number | string | boolean;
type Numeric = Exclude<T, string | boolean>; // number
Extract<T, U>
- 从类型T中提取可赋值给类型U的类型
ts
type T = string | number | boolean;
type StringOnly = Extract<T, string>; // string
type Numeric = Extract<T, number | bigint>; // number
NonNullable<T>
- 从类型T中排除null和undefined
ts
type Nullable = string | null | undefined;
type SafeString = NonNullable<Nullable>; // string
4. 函数类型工具
ReturnType<T>
- 获取函数类型的返回类型
ts
type Fn = () => Promise<string>;
type Result = ReturnType<Fn>; // Promise<string>
Parameters<T>
- 获取函数类型的参数元组类型
ts
type Handler = (req: Request, res: Response) => void;
type HandlerArgs = Parameters<Handler>; // [Request, Response]
ConstructorParameters<T>
- 获取构造函数类型的参数元组类型
ts
class User {
constructor(public id: number, public name: string) {}
}
type UserParams = ConstructorParameters<typeof User>; // [number, string]
二、TS进阶!基于内置类型的高阶TS扩展🚀🚀🚀
1. 基础类型扩展🏍🏍🏍
KeyOf<T>
- 获取类型 T 的所有键名的联合类型
ts
declare type KeyOf<T> = keyof T;
// 示例
type Obj = { a: number; b: string };
type Keys = KeyOf<Obj>; // "a" | "b"
Awaitable<T>
- 获取异步函数的返回值类型
ts
declare type Awaitable<T> = T | Promise<T> | Awaitable<T>;
const a: Awaitable<number> = 42;
const b: Awaitable<number> = Promise.resolve(42);
ElementType<T>
- 获取数组类型的元素类型
ts
declare type ElementType<T> = T extends Array<infer U> ? U : never;
type A = ElementType<string[]>; // string
type B = ElementType<number>; // never
Arrayable<T>
- 可数组化类型,创建 T 或者 T 的数组类型
ts
declare type Arrayable<T> = T | T[];
const a: Arrayable<string> = "hello";
const b: Arrayable<string> = ["hello", "world"];
ToArray<T>
- 将类型 T 转换为数组类型,如果已经是数组则保持不变
ts
declare type ToArray<T> = T extends (infer U)[] ? U[] : T[];
type A = ToArray<string>; // string[]
type B = ToArray<number[]>; // number[]
ArgumentType<T>
- 获取函数类型的参数元组类型
ts
declare type ArgumentType<T> = T extends ((...args: infer A) => unknown) ? A : never;
type Fn = (a: number, b: string) => boolean;
type Args = ArgumentType<Fn>; // [number, string]
Shift<T>
- 移除元组类型的第一个元素
ts
declare type Shift<T> = T extends [_: unknown, ...args: infer A] ? A : never;
type A = Shift<[number, string, boolean]>; // [string, boolean]
Pop<T>
- 移除元组类型的最后一个元素
ts
declare type Pop<T extends unknown[]> = T extends [...infer Rest, unknown] ? Rest : never;
type A = Pop<[number, string, boolean]>; // [number, string]
FlatObjectTuple<T>
- 扁平化对象元组类型,用于解决交叉类型显示问题
ts
declare type FlatObjectTuple<T> = { [K in keyof T]: T[K]; };
2. 中级进阶扩展✈️✈️✈️
(1) 类型断言工具
AssertEqual<T, U>
- 断言两个类型是否相等
ts
declare type AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;
(2) 深度类型转换
DeepPartial<T>
- 深度可选类型,递归地将所有属性设为可选
ts
declare type DeepPartial<T> = { [P in keyof T]?: DeepPartial<T[P]>; };
type Obj = { a: { b: number; c: string } };
type PartialObj = DeepPartial<Obj>; // { a?: { b?: number; c?: string } };
(3) 对象属性控制
PartialByKeys<T, K>
- 部分属性可选类型
ts
declare type PartialByKeys<T, K extends keyof T = keyof T> = FlatObjectTuple<Partial<Pick<T, Extract<keyof T, K>>> & Omit<T, K>>;
type Obj = { a: number; b: string; c: boolean };
type PartialA = PartialByKeys<Obj, 'a' | 'b'>; // { a?: number; b?: string; c: boolean };
RequiredByKey<T, K>
- 部分属性必需类型
ts
declare type RequiredByKey<T, K extends keyof T = keyof T> = FlatObjectTuple<Required<Pick<T, Extract<keyof T, K>>> & Omit<T, K>>;
type Obj = { a?: number; b?: string; c?: boolean };
type RequiredA = RequiredByKey<Obj, 'a' | 'b'>; // { a: number; b: string; c?: boolean };
3. 高级函数工具🚀🚀🚀
FunctionKeysObject<T>
- 提取对象中所有函数类型的属性
ts
declare type FunctionKeysObject<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never
}
FunctionMapType<F>
- 创建类型安全的函数事件系统
ts
declare type FunctionMapType<F> = <T extends keyof FunctionKeysObject<F>>(
key: T,
...args: Parameters<F[T]>
) => void
interface EventFunctions {
a(): void
b(params: { c: string; d: number }): void
}
declare const emit: FunctionMapType<EventFunctions>
emit('a') // 正确
emit('b', { c: '123', d: 123 }) // 正确
emit('b', { c: 123, d: '123' }) // 错误: 类型不匹配
emit('b', { c: 0, d: 1 }) // 错误: 类型不匹配
结语
类型就是文档,类型就是约束,类型就是力量!
TypeScript 的工具类型系统就像是一个强大类型转换工厂,内置工具类型提供基础能力,而基于它们实现的高阶扩展则可以用更优雅、更安全的方式构建复杂系统。 ✨✨✨
各位看官,如果感觉学到了,烦请抬起优雅的小手,给咱来个三连~~~ 👍🪙❤️
PS:转载请注明出处。