TypeScript的类型系统是图灵完备的,这意味着你可以用类型来做”计算”。这种技巧被称为”类型体操”。本文从基础开始,带你掌握TypeScript高级类型推导。
// 基本条件类型
type IsString = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实用:判断是否为数组
type IsArray = T extends any[] ? true : false;
type C = IsArray<string[]>; // true
type D = IsArray<string>; // false
// 提取数组元素类型
type ArrayElement = T extends (infer E)[] ? E : never;
type E = ArrayElement<string[]>; // string
type F = ArrayElement<number[]>; // number
// 提取函数返回类型
type ReturnType = T extends (...args: any[]) => infer R ? R : never;
type G = ReturnType<() => string>; // string
type H = ReturnType<(x: number) => boolean>; // boolean
// 提取Promise值类型
type PromiseValue = T extends Promise<infer V> ? V : T;
type I = PromiseValue<Promise<string>>; // string
type J = PromiseValue<string>; // string
// 深层Readonly
type DeepReadonly = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
interface User {
name: string;
profile: {
age: number;
address: {
city: string;
};
};
}
type ReadonlyUser = DeepReadonly<User>;
// 所有属性都变成readonly
// 深层Required
type DeepRequired = {
[K in keyof T]-?: T[K] extends object
? DeepRequired<T[K]>
: T[K];
};
type Length = T extends { length: infer L } ? L : never;
type Len = Length<[1, 2, 3]>; // 3
type StrLen = Length<'hello'>; // 5
type TupleToUnion = T extends (infer E)[] ? E : never;
type Union = TupleToUnion<['a', 'b', 'c']>; // 'a' | 'b' | 'c'
// 这个比较复杂,需要借助函数类型
type UnionToIntersection =
(T extends any ? (x: T) => void : never) extends
(x: infer R) => void ? R : never;
type LastOfUnion =
UnionToIntersection<T extends any ? () => T : never> extends
() => infer R ? R : never;
type Push = [...T, V];
type UnionToTuple =
T extends never ? [] :
Push<UnionToTuple<Exclude<T, LastOfUnion>>, LastOfUnion>;
type Tuple = UnionToTuple<'a' | 'b' | 'c'>; // ['a', 'b', 'c']
type Path =
K extends keyof T
? T[K] extends object
? K | `${K}.${Path<T[K]>}`
: K
: never;
interface Config {
server: {
host: string;
port: number;
};
database: {
url: string;
credentials: {
user: string;
password: string;
};
};
}
type ConfigPath = Path<Config>;
// 'server' | 'database' | 'server.host' | 'server.port' |
// 'database.url' | 'database.credentials' |
// 'database.credentials.user' | 'database.credentials.password'
type ObjectKeys = keyof T;
function safeKeys<T extends object>(obj: T): ObjectKeys[] {
return Object.keys(obj) as ObjectKeys[];
}
const user = { name: 'Alice', age: 30 };
const keys = safeKeys(user); // ('name' | 'age')[]
// 1. 可选属性变必选
type RequiredKeys = {
[K in keyof T]-?: T[K];
};
// 2. 选取特定类型的属性
type PickByType = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
active: boolean;
count: number;
}
type NumberProps = PickByType<Mixed, number>;
// { age: number; count: number }
// 3. 排除特定类型的属性
type OmitByType = {
[K in keyof T as T[K] extends U ? never : K]: T[K];
};
// 4. 函数参数类型
type Parameters = T extends (...args: infer P) => any ? P : never;
type Params = Parameters<(a: string, b: number) => void>;
// [string, number]
// 5. 可变元组操作
type Shift = T extends [infer _, ...infer R] ? R : [];
type First = T extends [infer F, ...any[]] ? F : never;
type Push = [...T, V];
type Pop = T extends [...infer R, infer _] ? R : [];
type Arr = [1, 2, 3];
type Shifted = Shift<Arr>; // [2, 3]
type FirstEl = First<Arr>; // 1
type Pushed = Push<Arr, 4>; // [1, 2, 3, 4]
type Popped = Pop<Arr>; // [1, 2]
// 定义验证规则类型
type Validator<T> = (value: T) => boolean;
type Validators<T> = {
[K in keyof T]?: Validator<T[K]>;
};
// 定义错误类型
type Errors<T> = {
[K in keyof T]?: string;
};
// 类型安全的表单验证器
function createValidator<T extends object>(
validators: Validators<T>
) {
return (data: T): Errors<T> => {
const errors: Errors<T> = {};
for (const key in validators) {
const validator = validators[key];
if (validator && !validator(data[key])) {
errors[key] = `Invalid ${String(key)}`;
}
}
return errors;
};
}
// 使用
interface LoginForm {
email: string;
password: string;
}
const validateLogin = createValidator<LoginForm>({
email: (v) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
password: (v) => v.length >= 8,
});
const errors = validateLogin({
email: 'invalid',
password: '123',
});
// { email: 'Invalid email', password: 'Invalid password' }
复杂的类型推导会增加编译时间:
// 避免:过深的递归
type DeepFlatten = T extends any[]
? DeepFlatten<T[number]>
: T; // 可能导致编译器崩溃
// 推荐:限制递归深度
type DeepFlattenSafe<
T,
Depth extends any[] = []
> = Depth['length'] extends 10
? T
: T extends any[]
? DeepFlattenSafe<T[number], [...Depth, 0]>
: T;
TypeScript类型体操的核心技巧:
💡 工具推荐:如果你在开发中需要处理大量数据文件,可以试试DataForge Pro——一个轻量级数据处理工具,比Excel快100倍,支持百万行数据的实时搜索和转换。
本文首发于 WD Tech Blog,转载请注明出处。