TypeScript 提供了一系列内置的工具类型(Utility Types),用于常见的类型转换操作。这些工具类型基于映射类型和条件类型实现,能够帮助我们高效地创建新的类型。本章将详细介绍所有内置工具类型的使用方法,并展示如何创建自定义工具类型。
将所有属性变为可选的。
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 使用示例
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 创建用户时,id 由数据库生成
type CreateUserDTO = Partial<User>;
// {
// id?: number;
// name?: string;
// email?: string;
// age?: number;
// }
// 更精确:只让部分属性可选
type CreateUserInput = Omit<User, "id"> & Partial<Pick<User, "age">>;
// {
// name: string;
// email: string;
// age?: number;
// }
// 实际应用:更新函数
function updateUser(user: User, updates: Partial<User>): User {
return { ...user, ...updates };
}
const user: User = { id: 1, name: "Alice", email: "alice@example.com", age: 25 };
const updated = updateUser(user, { age: 26 }); // OK,只更新 age
将所有属性变为必需的(移除可选修饰符)。
type Required<T> = {
[P in keyof T]-?: T[P];
};
// 使用示例
interface Config {
host: string;
port?: number;
ssl?: boolean;
timeout?: number;
}
type StrictConfig = Required<Config>;
// {
// host: string;
// port: number;
// ssl: boolean;
// timeout: number;
// }
// 实际应用:配置合并后的校验
function validateConfig(config: Required<Config>): boolean {
return config.port > 0 && config.port < 65536 && config.timeout > 0;
}
const fullConfig: Required<Config> = {
host: "localhost",
port: 3000,
ssl: false,
timeout: 5000
};
将所有属性变为只读的。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 使用示例
interface Point {
x: number;
y: number;
}
type ImmutablePoint = Readonly<Point>;
// {
// readonly x: number;
// readonly y: number;
// }
const point: ImmutablePoint = { x: 0, y: 0 };
// point.x = 1; // Error: Cannot assign to 'x' because it is a read-only property
// 实际应用:常量配置
const API_CONFIG: Readonly<{
baseURL: string;
version: string;
timeout: number;
}> = {
baseURL: "https://api.example.com",
version: "v1",
timeout: 5000
};
// API_CONFIG.timeout = 10000; // Error
从类型 T 中选取一组属性 K 组成新类型。
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// 使用示例
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
updatedAt: Date;
}
// 创建公开的用户信息类型(排除敏感字段)
type PublicUser = Pick<User, "id" | "name" | "email">;
// {
// id: number;
// name: string;
// email: string;
// }
// API 响应类型
type UserResponse = Pick<User, "id" | "name" | "email" | "createdAt">;
function getPublicUser(user: User): PublicUser {
return {
id: user.id,
name: user.name,
email: user.email
};
}
从类型 T 中排除一组属性 K 组成新类型。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// 使用示例
interface User {
id: number;
name: string;
email: string;
password: string;
internalNotes: string;
}
// 排除敏感和内部字段
type SafeUser = Omit<User, "password" | "internalNotes">;
// {
// id: number;
// name: string;
// email: string;
// }
// 创建用户请求(排除自动生成的字段)
type CreateUserRequest = Omit<User, "id">;
// 更新用户请求(排除 id 和所有可选处理)
type UpdateUserRequest = Partial<Omit<User, "id">>;
// 实际应用:DTO 转换
class UserDTO {
static toSafeUser(user: User): SafeUser {
const { password, internalNotes, ...safeUser } = user;
return safeUser;
}
}
从 T 中排除可以赋值给 U 的类型。
type Exclude<T, U> = T extends U ? never : T;
// 使用示例
type AllStatus = "pending" | "success" | "error" | "loading";
type FinalStatus = Exclude<AllStatus, "pending" | "loading">;
// "success" | "error"
// 从联合类型中排除 null/undefined
type MaybeString = string | null | undefined;
type DefinitelyString = Exclude<MaybeString, null | undefined>;
// string
// 实际应用:状态机转换
type State =
| { status: "idle" }
| { status: "loading"; requestId: string }
| { status: "success"; data: any }
| { status: "error"; error: Error };
type NonIdleState = Exclude<State, { status: "idle" }>;
// { status: "loading" } | { status: "success" } | { status: "error" }
从 T 中提取可以赋值给 U 的类型。
type Extract<T, U> = T extends U ? T : never;
// 使用示例
type AllEvents = "click" | "hover" | "scroll" | "resize" | "keydown";
type MouseEvents = Extract<AllEvents, "click" | "hover">;
// "click" | "hover"
// 提取特定类型的属性名
type StringProperties<T> = {
[K in keyof T]: T[K] extends string ? K : never;
}[keyof T];
interface Person {
name: string;
age: number;
email: string;
isActive: boolean;
}
type StringKeys = StringProperties<Person>;
// "name" | "email"
从 T 中排除 null 和 undefined。
type NonNullable<T> = T extends null | undefined ? never : T;
// 使用示例
type MaybeNumber = number | null | undefined;
type DefinitelyNumber = NonNullable<MaybeNumber>;
// number
// 实际应用:过滤数组中的空值
function filterNonNull<T>(items: (T | null | undefined)[]): T[] {
return items.filter((item): item is T => item !== null && item !== undefined);
}
const maybeNumbers: (number | null)[] = [1, null, 2, null, 3];
const numbers = filterNonNull(maybeNumbers);
// number[]
提取函数类型的参数类型,以元组形式返回。
type Parameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
// 使用示例
function createUser(name: string, email: string, age?: number): { id: number } {
return { id: 1 };
}
type CreateUserParams = Parameters<typeof createUser>;
// [name: string, email: string, age?: number]
// 提取第一个参数
type FirstParameter<T extends (...args: any) => any> =
Parameters<T> extends [infer P, ...any[]] ? P : never;
// 提取除第一个外的参数
type RestParameters<T extends (...args: any) => any> =
Parameters<T> extends [any, ...infer P] ? P : never;
// 实际应用:高阶函数类型安全
function withLogging<T extends (...args: any[]) => any>(
fn: T
): (...args: Parameters<T>) => ReturnType<T> {
return (...args) => {
console.log("Calling function with:", args);
return fn(...args);
};
}
const loggedCreateUser = withLogging(createUser);
// 类型: (name: string, email: string, age?: number) => { id: number }
提取函数类型的返回类型。
type ReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;
// 使用示例
async function fetchUser(id: number) {
return {
id,
name: "Alice",
email: "alice@example.com"
};
}
type FetchUserReturn = ReturnType<typeof fetchUser>;
// Promise<{ id: number; name: string; email: string; }>
// 提取 Promise 的解析类型
type Awaited<T> = T extends Promise<infer R> ? R : T;
type UserData = Awaited<FetchUserReturn>;
// { id: number; name: string; email: string; }
// 实际应用:API 客户端类型
class ApiClient {
async request<T>(url: string): Promise<T> {
const response = await fetch(url);
return response.json();
}
}
const client = new ApiClient();
type ApiRequestReturn<T> = ReturnType<ApiClient["request"]> extends Promise<infer R>
? R
: never;
处理函数中的 this 参数。
// 提取 this 参数的类型
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any
? U
: unknown;
// 移除 this 参数
type OmitThisParameter<T> = unknown extends ThisParameterType<T>
? T
: T extends (this: any, ...args: infer A) => infer R
? (...args: A) => R
: T;
// 使用示例
function greet(this: { name: string }, greeting: string): string {
return `${greeting}, ${this.name}!`;
}
type GreetThis = ThisParameterType<typeof greet>;
// { name: string }
type GreetWithoutThis = OmitThisParameter<typeof greet>;
// (greeting: string) => string
提取构造函数类型的参数类型。
type ConstructorParameters<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: infer P) => any ? P : never;
// 使用示例
class User {
constructor(
public name: string,
public age: number,
public email?: string
) {}
}
type UserConstructorParams = ConstructorParameters<typeof User>;
// [name: string, age: number, email?: string]
// 实际应用:工厂函数
function createUser(...args: ConstructorParameters<typeof User>): User {
return new User(...args);
}
const user = createUser("Alice", 25, "alice@example.com");
提取构造函数类型的实例类型。
type InstanceType<T extends abstract new (...args: any) => any> =
T extends abstract new (...args: any) => infer R ? R : any;
// 使用示例
type UserInstance = InstanceType<typeof User>;
// User
// 实际应用:依赖注入容器
type ClassConstructor<T = any> = new (...args: any[]) => T;
class Container {
private instances = new Map<string, any>();
register<T>(key: string, Constructor: ClassConstructor<T>): void {
this.instances.set(key, new Constructor());
}
get<T>(key: string): T {
return this.instances.get(key);
}
}
const container = new Container();
container.register("user", User);
const userService = container.get<User>("user");
// Uppercase - 转大写
type HTTPMethod = "get" | "post";
type UpperMethods = Uppercase<HTTPMethod>;
// "GET" | "POST"
// Lowercase - 转小写
type LowerMethods = Lowercase<"GET" | "POST">;
// "get" | "post"
// Capitalize - 首字母大写
type EventNames = "click" | "hover";
type HandlerNames = `on${Capitalize<EventNames>}`;
// "onClick" | "onHover"
// Uncapitalize - 首字母小写
type PropertyNames = "Name" | "Age";
type CamelCase = `get${PropertyNames}`;
// "getName" | "getAge"
// 提取事件名前缀
type EventPrefix<T extends string> =
T extends `${infer Prefix}${string}` ? Prefix : never;
type ClickPrefix = EventPrefix<"onClick">; // "on"
// 提取 camelCase 的各个部分
type SplitCamelCase<S extends string> =
S extends `${infer Head}${infer Tail}`
? Head extends Uppercase<Head>
? [Head, ...SplitCamelCase<Tail>]
: [Head, ...SplitCamelCase<Tail>]
: [];
// 字符串路径提取
type ExtractPathParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? { [K in Param]: string } & ExtractPathParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? { [K in Param]: string }
: {};
type RouteParams = ExtractPathParams<"/users/:id/posts/:postId">;
// { id: string; postId: string; }
// 深度 Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 深度 Required
type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};
// 深度 Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
interface Nested {
a: {
b: {
c: string;
};
};
}
type PartialNested = DeepPartial<Nested>;
// { a?: { b?: { c?: string; }; }; }
// 获取指定类型的键
type KeysOfType<T, U> = {
[K in keyof T]: T[K] extends U ? K : never;
}[keyof T];
interface User {
id: number;
name: string;
email: string;
age: number;
isActive: boolean;
}
type StringKeys = KeysOfType<User, string>;
// "name" | "email"
// 提取指定类型的属性
type PickByType<T, U> = Pick<T, KeysOfType<T, U>>;
type StringProps = PickByType<User, string>;
// { name: string; email: string; }
// 排除指定类型的属性
type OmitByType<T, U> = Pick<T, Exclude<keyof T, KeysOfType<T, U>>>;
type NonStringProps = OmitByType<User, string>;
// { id: number; age: number; isActive: boolean; }
// 可为 null 的属性
type Nullable<T> = { [P in keyof T]: T[P] | null };
// 移除 readonly
type Mutable<T> = { -readonly [P in keyof T]: T[P] };
// 联合类型转交叉类型
type UnionToIntersection<T> =
(T extends any ? (x: T) => void : never) extends
(x: infer R) => void ? R : never;
type Mixed = { a: 1 } | { b: 2 };
type Intersection = UnionToIntersection<Mixed>;
// { a: 1; b: 2; }
// 联合类型转元组(保留顺序)
type UnionToTuple<T, L = LastInUnion<T>> =
[T] extends [never]
? []
: [...UnionToTuple<Exclude<T, L>>, L];
type LastInUnion<T> =
UnionToIntersection<T extends any ? (x: T) => void : never> extends
(x: infer L) => void ? L : never;
type T = UnionToTuple<"a" | "b" | "c">;
// ["a", "b", "c"] 或其他顺序
// 判断类型是否为 never
type IsNever<T> = [T] extends [never] ? true : false;
// 判断类型是否为 any
type IsAny<T> = 0 extends (1 & T) ? true : false;
// Action 创建函数类型
interface Action<T extends string = string, P = any> {
type: T;
payload: P;
}
type ActionCreator<T extends string, P> =
undefined extends P
? () => Action<T, P>
: (payload: P) => Action<T, P>;
// 从 action creators 对象推断 action 类型
type ActionsUnion<A extends Record<string, (...args: any[]) => Action>> =
ReturnType<A[keyof A]>;
// 使用
const userActions = {
fetchUser: (id: number) => ({
type: "FETCH_USER" as const,
payload: id
}),
updateUser: (user: { id: number; name: string }) => ({
type: "UPDATE_USER" as const,
payload: user
}),
deleteUser: (id: number) => ({
type: "DELETE_USER" as const,
payload: id
})
};
type UserAction = ActionsUnion<typeof userActions>;
// { type: "FETCH_USER"; payload: number; } |
// { type: "UPDATE_USER"; payload: { id: number; name: string; }; } |
// { type: "DELETE_USER"; payload: number; }
// 端点定义
interface Endpoints {
"/users": {
GET: { response: User[] };
POST: { body: CreateUserDTO; response: User };
};
"/users/:id": {
GET: { params: { id: string }; response: User };
PUT: { params: { id: string }; body: UpdateUserDTO; response: User };
DELETE: { params: { id: string }; response: void };
};
}
// 提取路径参数
type ExtractParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? { [K in Param]: string } & ExtractParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? { [K in Param]: string }
: {};
// 类型安全请求函数
type RequestMethod = "GET" | "POST" | "PUT" | "DELETE";
async function apiRequest<
Path extends keyof Endpoints,
Method extends keyof Endpoints[Path]
>(
path: Path,
method: Method,
...args: Method extends "GET"
? Endpoints[Path][Method] extends { params: infer P }
? [P]
: []
: Endpoints[Path][Method] extends { params: infer P; body: infer B }
? [P, B]
: Endpoints[Path][Method] extends { body: infer B }
? [B]
: []
): Promise<Endpoints[Path][Method] extends { response: infer R } ? R : never> {
// 实现...
return null as any;
}
// 使用
const users = await apiRequest("/users", "GET");
const user = await apiRequest("/users/:id", "GET", { id: "1" });
本章我们学习了 TypeScript 的内置工具类型:
1. Partial<T> - 所有属性可选
2. Required<T> - 所有属性必需
3. Readonly<T> - 所有属性只读
4. Pick<T, K> - 选取指定属性
5. Omit<T, K> - 排除指定属性
6. Exclude<T, U> - 从联合类型排除
7. Extract<T, U> - 从联合类型提取
8. NonNullable<T> - 排除 null 和 undefined
9. Parameters<T> - 提取函数参数
10. ReturnType<T> - 提取函数返回类型
11. ConstructorParameters<T> - 提取构造函数参数
12. InstanceType<T> - 提取实例类型
13. 字符串工具类型 - Uppercase、Lowercase、Capitalize、Uncapitalize
以及自定义工具类型的创建方法。
给定以下接口,创建相应的 DTO 类型:
interface Product {
id: string;
name: string;
price: number;
description: string;
createdAt: Date;
updatedAt: Date;
internalNotes: string;
}
// 1. 创建产品请求(排除 id、时间戳和内部字段)
// 2. 更新产品请求(所有字段可选,排除 id)
// 3. 公开产品信息(只包含 name、price、description)
// 4. 产品列表项(公开信息 + id)
参考答案
// 1. 创建产品请求 type CreateProductDTO = Omit<Product, "id" | "createdAt" | "updatedAt" | "internalNotes">; // 2. 更新产品请求 type UpdateProductDTO = Partial<Omit<Product, "id" | "createdAt" | "updatedAt" | "internalNotes">>; // 3. 公开产品信息 type PublicProduct = Pick<Product, "name" | "price" | "description">; // 4. 产品列表项 type ProductListItem = Pick<Product, "id" | "name" | "price" | "description">; // 或者使用组合 type ProductListItem2 = Pick<Product, "id"> & PublicProduct;
实现以下工具类型:
// 1. 将对象的所有属性路径展平为联合类型
// 例如:{ a: { b: number; c: string } } => "a" | "a.b" | "a.c"
type Paths<T> = any;
// 2. 根据路径获取对象类型
type PathValue<T, P extends string> = any;
// 3. 创建响应式引用类型(类似 Vue 的 Ref)
type Ref<T> = any;
参考答案
// 1. 属性路径
type Paths<T, K extends keyof T = keyof T> =
K extends string
? T[K] extends Record<string, any>
? K | `${K}.${Paths<T[K]>}`
: K
: never;
interface Nested {
user: {
name: string;
address: {
city: string;
};
};
}
type NestedPaths = Paths<Nested>;
// "user" | "user.name" | "user.address" | "user.address.city"
// 2. 路径值获取
type PathValue<T, P extends string> =
P extends `${infer K}.${infer Rest}`
? K extends keyof T
? PathValue<T[K], Rest>
: never
: P extends keyof T
? T[P]
: never;
type City = PathValue<Nested, "user.address.city">; // string
// 3. 响应式引用
type Ref<T> = { value: T };
function ref<T>(value: T): Ref<T> {
return { value };
}
const count = ref(0); // Ref<number>
const user = ref({ name: "Alice" }); // Ref<{ name: string; }>
使用工具类型实现类型安全的表单验证:
// 要求: // 1. 根据数据模型自动生成验证规则类型 // 2. 验证结果类型包含每个字段的错误信息 // 3. 验证函数返回类型安全的结果
参考答案
// 验证规则
interface ValidationRule<T> {
required?: boolean;
min?: number;
max?: number;
pattern?: RegExp;
custom?: (value: T) => string | undefined;
}
// 验证器映射
type ValidatorMap<T> = {
[K in keyof T]?: ValidationRule<T[K]>;
};
// 验证错误
type ValidationErrors<T> = {
[K in keyof T]?: string;
};
// 验证结果
type ValidationResult<T> =
| { valid: true; errors: null }
| { valid: false; errors: ValidationErrors<T> };
// 验证器类
class FormValidator<T extends Record<string, any>> {
constructor(private rules: ValidatorMap<T>) {}
validate(data: T): ValidationResult<T> {
const errors: ValidationErrors<T> = {};
for (const key in this.rules) {
const rule = this.rules[key];
const value = data[key];
if (!rule) continue;
// 必填验证
if (rule.required && (value === undefined || value === null || value === "")) {
errors[key] = `${String(key)} is required`;
continue;
}
// 数值验证
if (typeof value === "number") {
if (rule.min !== undefined && value < rule.min) {
errors[key] = `${String(key)} must be at least ${rule.min}`;
}
if (rule.max !== undefined && value > rule.max) {
errors[key] = `${String(key)} must be at most ${rule.max}`;
}
}
// 字符串验证
if (typeof value === "string") {
if (rule.pattern && !rule.pattern.test(value)) {
errors[key] = `${String(key)} format is invalid`;
}
}
// 自定义验证
if (rule.custom) {
const error = rule.custom(value);
if (error) {
errors[key] = error;
}
}
}
const isValid = Object.keys(errors).length === 0;
return isValid
? { valid: true, errors: null }
: { valid: false, errors };
}
}
// 使用
interface LoginForm {
email: string;
password: string;
rememberMe: boolean;
}
const loginValidator = new FormValidator<LoginForm>({
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
password: {
required: true,
custom: (value) => value.length < 8 ? "Password must be at least 8 characters" : undefined
}
});
const result = loginValidator.validate({
email: "invalid",
password: "short",
rememberMe: true
});
if (!result.valid) {
console.log(result.errors.email); // string | undefined
console.log(result.errors.password); // string | undefined
}