0%

TypeScript高级类型编程——类型安全的极限实践

TypeScript的高级类型系统提供了强大的类型编程能力,通过巧妙运用条件类型、映射类型等特性,可以实现几乎完美的类型安全,将错误提前到编译时捕获。

介绍

  TypeScript作为JavaScript的超集,其最大的价值在于提供了静态类型检查的能力。随着版本的迭代,TypeScript的类型系统变得越来越强大,特别是高级类型特性让开发者能够在类型层面进行编程,实现复杂的类型操作和约束。本文将深入探讨TypeScript高级类型编程的各个方面,从基础概念到实战应用,帮助读者掌握类型安全的极限实践。

高级类型基础

条件类型 (Conditional Types)

条件类型允许我们在类型层面进行条件判断,根据类型关系选择不同的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 基础条件类型语法
type IfEquals<X, Y, A = X, B = Y> = (<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;

// 实用的条件类型示例
type IsString<T> = T extends string ? true : false;

type StringOrNumber<T> = T extends string
? string
: T extends number
? number
: never;

// 条件类型在函数中的应用
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}

const person = { name: "Alice", age: 30 };
const name = getProperty(person, "name"); // 类型为 string
const age = getProperty(person, "age"); // 类型为 number

分布式条件类型 (Distributive Conditional Types)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 分布式条件类型会自动分布在联合类型上
type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>; // string[] | number[]

// 非分布式条件类型(通过元组实现)
type NonDistributedToArray<T> = [T] extends [any] ? T[] : never;

type NonDistResult = NonDistributedToArray<string | number>; // (string | number)[]

// 实用的分布式条件类型
type FilterFlags<Base, Condition> = {
[Key in keyof Base]: Base[Key] extends Condition ? Key : never;
};

type AllowedNames = FilterFlags<{
name: string;
age: number;
active: boolean;
email: string;
}, string>;

// 期望结果: { name: "name"; email: "email" }

type KeysMatching<T, Condition> = {
[K in keyof T]: T[K] extends Condition ? K : never;
}[keyof T];

type StringKeys = KeysMatching<{
name: string;
age: number;
active: boolean;
}, string>; // "name"

映射类型高级应用

高级映射类型模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 深度只读映射类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly<T[P]>
: T[P];
};

type NestedObj = {
name: string;
details: {
age: number;
address: {
street: string;
city: string;
};
};
};

type ReadonlyNestedObj = DeepReadonly<NestedObj>;
// 所有层级都是只读的

// 条件映射类型 - 根据属性类型添加修饰符
type ConditionalPartial<T, K extends keyof T> = {
[P in keyof T]: P extends K ? Partial<T[P]> : T[P];
};

type User = {
id: number;
name: string;
email: string;
preferences: {
theme: string;
notifications: boolean;
};
};

type PartialPreferencesUser = ConditionalPartial<User, 'preferences'>;
// preferences 变为可选,其他保持不变

// 映射类型修饰符运算
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};

type Concrete<Type> = {
[Property in keyof Type]-?: Type[Property];
};

type MaybeUser = {
id: number;
name?: string;
email?: string;
};

type ConcreteUser = Concrete<MaybeUser>;
// 所有属性都变为必需的

高级映射类型实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 创建响应式数据类型
type Reactive<T> = {
readonly [K in keyof T]: T[K] extends object
? T[K] extends Function
? T[K]
: Reactive<T[K]>
: T[K];
} & {
__isReactive: true;
};

// 深度键路径类型
type DeepKeyOf<T> = T extends object
? { [K in keyof T]: K extends string
? T[K] extends object
? `${K}` | `${K}.${DeepKeyOf<T[K]>}`
: `${K}`
: never
}[keyof T]
: never;

type ApiResponse = {
user: {
profile: {
name: string;
age: number;
};
contacts: {
email: string;
phone: string;
};
};
settings: {
theme: string;
language: string;
};
};

type ResponseKeys = DeepKeyOf<ApiResponse>;
// "user" | "user.profile" | "user.profile.name" | "user.profile.age" |
// "user.contacts" | "user.contacts.email" | "user.contacts.phone" |
// "settings" | "settings.theme" | "settings.language"

// 路径获取类型
type PathValue<T, P extends string> = P extends keyof T
? T[P]
: P extends `${infer K}.${infer R}`
? K extends keyof T
? PathValue<T[K], R>
: never
: never;

type ProfileName = PathValue<ApiResponse, 'user.profile.name'>; // string
type ContactsEmail = PathValue<ApiResponse, 'user.contacts.email'>; // string

泛型高级技巧

复杂泛型约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 泛型约束与条件类型的结合
type AsyncReturnType<T extends (...args: any) => Promise<any>> =
T extends (...args: any) => Promise<infer R> ? R : any;

async function fetchUser(id: number): Promise<{ id: number; name: string }> {
return { id, name: 'User' };
}

type UserType = AsyncReturnType<typeof fetchUser>;
// { id: number; name: string }

// 泛型工厂模式
type Constructor<T = {}> = new (...args: any[]) => T;

type WithObservableState<T> = T & {
subscribe: (callback: (state: T) => void) => () => void;
getState: () => T;
setState: (partial: Partial<T>) => void;
};

function makeObservable<T extends Constructor>(
Base: T
): Constructor<InstanceType<T> & WithObservableState<InstanceType<T>>> {
return class extends Base {
private observers: Array<(state: any) => void> = [];
private currentState: any;

subscribe(callback: (state: any) => void) {
this.observers.push(callback);
return () => {
const index = this.observers.indexOf(callback);
if (index > -1) {
this.observers.splice(index, 1);
}
};
}

getState() {
return this.currentState;
}

setState(partial: Partial<any>) {
this.currentState = { ...this.currentState, ...partial };
this.observers.forEach(observer => observer(this.currentState));
}
};
}

// 泛型约束的推导
type ValidateAndTransform<Input, Schema> = {
[K in keyof Schema]: Schema[K] extends (value: infer T) => any
? T extends Input[Extract<K, keyof Input>]
? ReturnType<Schema[K]>
: never
: Input[Extract<K, keyof Input>];
};

type UserInput = {
name: string;
age: number;
email: string;
};

type UserValidator = {
name: (name: string) => string;
age: (age: number) => number;
email: (email: string) => string;
};

type ValidatedUser = ValidateAndTransform<UserInput, UserValidator>;
// 保持相同的结构但应用转换函数的返回类型

泛型与元编程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 类型级别的函数式编程
type MapTypes<T, Mapper> = {
[K in keyof T]: T[K] extends keyof Mapper
? Mapper[T[K]]
: T[K];
};

type SourceType = {
name: 'string';
age: 'number';
active: 'boolean';
};

type TypeMappings = {
'string': string;
'number': number;
'boolean': boolean;
'array': any[];
};

type MappedType = MapTypes<SourceType, TypeMappings>;
// { name: string; age: number; active: boolean; }

// 类型级别的 reduce 操作
type Flatten<T> = T extends Array<infer U>
? U extends Array<any>
? Flatten<U>
: U
: T;

type NestedArray = number[][][][];
type FlatNumber = Flatten<NestedArray>; // number

// 类型级别的 filter 操作
type FilterByValueType<T, V> = {
[K in keyof T as T[K] extends V ? K : never]: T[K];
};

type MixedObject = {
name: string;
age: number;
email: string;
active: boolean;
};

type StringFields = FilterByValueType<MixedObject, string>;
// { name: string; email: string; }

实用类型工具

类型推断与转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 高级类型推断工具
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends
((k: infer I) => void) ? I : never;

type Union = { a: string } | { b: number } | { c: boolean };
type Intersection = UnionToIntersection<Union>;
// { a: string } & { b: number } & { c: boolean }

// 函数参数类型提取
type GetParameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;

type GetReturnType<T extends (...args: any[]) => any> =
T extends (...args: any) => infer R ? R : any;

type GetFirstParameter<T extends (...args: any[]) => any> =
T extends (first: infer F, ...args: any[]) => any ? F : never;

// 示例函数
function exampleFunction(
name: string,
age: number,
isActive: boolean
): { success: boolean; data: string } {
return { success: true, data: 'result' };
}

type ExampleParams = GetParameters<typeof exampleFunction>;
// [name: string, age: number, isActive: boolean]

type ExampleReturn = GetReturnType<typeof exampleFunction>;
// { success: boolean; data: string }

type FirstNameParam = GetFirstParameter<typeof exampleFunction>;
// string

高级工具类型实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
// 深度合并类型
type DeepMerge<T, U> = {
[K in keyof T | keyof U]: K extends keyof T
? K extends keyof U
? T[K] extends object
? U[K] extends object
? DeepMerge<T[K], U[K]>
: U[K]
: U[K]
: T[K]
: K extends keyof U
? U[K]
: never;
};

type ConfigA = {
server: {
port: number;
host: string;
};
features: {
auth: boolean;
};
};

type ConfigB = {
server: {
ssl: boolean;
};
features: {
logging: boolean;
};
};

type MergedConfig = DeepMerge<ConfigA, ConfigB>;
// {
// server: {
// port: number;
// host: string;
// ssl: boolean;
// };
// features: {
// auth: boolean;
// logging: boolean;
// };
// }

// 依赖注入类型系统
type Injectable<T> = T & { __inject?: any };

type ResolveDependencies<T> = {
[K in keyof T]: T[K] extends Injectable<infer U> ? U : T[K];
};

type ServiceDependencies = {
logger: Injectable<(message: string) => void>;
database: Injectable<{ connect: () => Promise<void> }>;
cache: Injectable<{ get: (key: string) => any }>;
};

type ResolvedServices = ResolveDependencies<ServiceDependencies>;
// 所有 Injectable 包装被移除

类型安全最佳实践

严格的类型约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 严格枚举类型
type StrictEnum<T extends Record<string, string | number>> = {
[K in keyof T]: T[K];
} & {
[K: string]: T[keyof T];
};

const StatusEnum = {
ACTIVE: 'ACTIVE',
INACTIVE: 'INACTIVE',
PENDING: 'PENDING',
} as const;

type Status = typeof StatusEnum[keyof typeof StatusEnum];
// "ACTIVE" | "INACTIVE" | "PENDING"

// 字面量类型保持
function createOption<T extends string>(value: T) {
return {
value,
label: value.charAt(0).toUpperCase() + value.slice(1)
};
}

const option = createOption('red'); // { value: "red"; label: "Red" }
// 保持了字面量类型 "red" 而不是 string

// 防止运行时类型污染
type Exact<T, Shape> = T extends Shape
? Exclude<keyof T, keyof Shape> extends never
? T
: never
: never;

// 这样可以确保对象不包含意外的额外属性
type ExpectedShape = { name: string; age: number };

function processUser(user: Exact<{ name: string; age: number; email?: string }, ExpectedShape>): void {
// 只有 name 和 age 是允许的
}

// processUser({ name: "John", age: 30 }); // ✓
// processUser({ name: "John", age: 30, email: "test" }); // ✗ 编译错误

运行时类型检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 编译时和运行时类型检查结合
interface TypeGuard<T> {
(value: unknown): value is T;
type: string;
}

class TypeValidator {
static create<T>(guard: (value: unknown) => value is T, typeName: string): TypeGuard<T> {
const validator: TypeGuard<T> = guard as TypeGuard<T>;
validator.type = typeName;
return validator;
}

static string = TypeValidator.create<string>(
(value: unknown): value is string => typeof value === 'string',
'string'
);

static number = TypeValidator.create<number>(
(value: unknown): value is number => typeof value === 'number' && !isNaN(value),
'number'
);

static boolean = TypeValidator.create<boolean>(
(value: unknown): value is boolean => typeof value === 'boolean',
'boolean'
);

static array<T>(elementValidator: TypeGuard<T>) {
return TypeValidator.create<T[]>(
(value: unknown): value is T[] => {
return Array.isArray(value) && value.every(item => elementValidator(item));
},
`Array<${elementValidator.type}>`
);
}

static object<T extends Record<string, TypeGuard<any>>>(
schema: T
): TypeGuard<{ [K in keyof T]: T[K] extends TypeGuard<infer U> ? U : never }> {
return TypeValidator.create(
(value: unknown): value is any => {
if (typeof value !== 'object' || value === null) return false;

for (const [key, validator] of Object.entries(schema)) {
if (!(key in value) || !validator((value as any)[key])) {
return false;
}
}
return true;
},
`Object<${Object.keys(schema).join(', ')}>`
);
}
}

// 使用类型验证器
const UserSchema = TypeValidator.object({
name: TypeValidator.string,
age: TypeValidator.number,
active: TypeValidator.boolean,
tags: TypeValidator.array(TypeValidator.string)
});

// 运行时验证
function validateUser(data: unknown): asserts data is {
name: string;
age: number;
active: boolean;
tags: string[];
} {
if (!UserSchema(data)) {
throw new Error('Invalid user data');
}
}

// 类型安全的API调用
type ApiEndpoint<TRequest, TResponse> = {
path: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
requestValidator: TypeGuard<TRequest>;
responseValidator: TypeGuard<TResponse>;
};

function createTypedApi<TRequest, TResponse>(
endpoint: Omit<ApiEndpoint<TRequest, TResponse>, 'requestValidator' | 'responseValidator'>,
requestValidator: TypeGuard<TRequest>,
responseValidator: TypeGuard<TResponse>
): ApiEndpoint<TRequest, TResponse> {
return {
...endpoint,
requestValidator,
responseValidator
};
}

// API使用示例
const updateUserEndpoint = createTypedApi(
{
path: '/api/users/:id',
method: 'PUT'
},
UserSchema, // 请求验证器
TypeValidator.object({ success: TypeValidator.boolean }) // 响应验证器
);

高级模式与技巧

类型级别的算法实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 类型级别的数值计算
type Add<A extends number, B extends number> =
[...NumTuple<A>, ...NumTuple<B>]['length'] & number;

type Sub<A extends number, B extends number> =
NumTuple<A> extends [...infer U, ...NumTuple<B>]
? U['length'] & number
: never;

type NumTuple<N extends number, T extends any[] = []> =
T['length'] extends N ? T : NumTuple<N, [...T, any]>;

// 类型级别的字符串操作
type Join<T extends string[], D extends string> =
T extends [infer F extends string, ...infer R extends string[]]
? R extends []
? F
: `${F}${D}${Join<R, D>}`
: '';

type Split<S extends string, D extends string> =
S extends `${infer T}${D}${infer U}`
? [T, ...Split<U, D>]
: [S];

type CapitalizeWord<S extends string> =
S extends `${infer First}${infer Rest}`
? `${Uppercase<First>}${Lowercase<Rest>}`
: S;

type CamelCase<S extends string> =
S extends `${infer First}_${infer Rest}`
? `${First}${CapitalizeWord<Rest>}`
: S;

type SnakeCase<S extends string> =
S extends `${infer First}${Uppercase<infer Rest>}`
? `${Lowercase<First>}_${Lowercase<Rest>}`
: Lowercase<S>;

// 示例
type TestJoin = Join<['hello', 'world', 'typescript'], '-'>; // "hello-world-typescript"
type TestSplit = Split<'hello-world-typescript', '-'>; // ["hello", "world", "typescript"]
type TestCamel = CamelCase<'hello_world_typescript'>; // "helloWorldTypescript"
type TestSnake = SnakeCase<'helloWorldTypescript'>; // "hello_world_typescript"

复杂业务场景的类型建模

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// 状态机类型建模
type StateMachine<States extends string, Events extends string> = {
[S in States]: {
[E in Events]?: States;
};
};

type AuthStates = 'unauthenticated' | 'authenticating' | 'authenticated' | 'error';
type AuthEvents = 'LOGIN' | 'LOGOUT' | 'LOGIN_SUCCESS' | 'LOGIN_ERROR';

type AuthStateMachine = StateMachine<AuthStates, AuthEvents>;

const authTransitions: AuthStateMachine = {
unauthenticated: {
LOGIN: 'authenticating'
},
authenticating: {
LOGIN_SUCCESS: 'authenticated',
LOGIN_ERROR: 'error'
},
authenticated: {
LOGOUT: 'unauthenticated'
},
error: {
LOGIN: 'authenticating'
}
};

// 类型安全的状态转换函数
function transition<S extends AuthStates, E extends AuthEvents>(
currentState: S,
event: E,
transitions: AuthStateMachine
): S extends keyof AuthStateMachine
? E extends keyof AuthStateMachine[S]
? AuthStateMachine[S][E] extends AuthStates
? AuthStateMachine[S][E]
: S // 如果没有定义转换,则保持当前状态
: S // 如果事件不在当前状态的转换中,则保持当前状态
: S {
const nextState = transitions[currentState]?.[event];
return (nextState || currentState) as any;
}

// 表单验证类型系统
type ValidationRule<T> = (value: T) => { valid: boolean; error?: string };

type FieldValidation<T> = {
[K in keyof T]: ValidationRule<T[K]>;
};

type ValidationResult<T> = {
[K in keyof T]: {
value: T[K];
valid: boolean;
error?: string;
};
};

type ValidateForm<T, V extends FieldValidation<T>> = (
formData: T,
validators: V
) => ValidationResult<T>;

// 实际的表单验证实现
function validateForm<T, V extends FieldValidation<T>>(
formData: T,
validators: V
): ValidationResult<T> {
const result = {} as ValidationResult<T>;

for (const key in validators) {
const value = formData[key];
const validator = validators[key];
const validation = validator(value);

result[key] = {
value,
valid: validation.valid,
...(validation.error && { error: validation.error })
};
}

return result;
}

// 使用示例
type LoginForm = {
username: string;
password: string;
rememberMe: boolean;
};

const loginValidators: FieldValidation<LoginForm> = {
username: (value) => ({
valid: value.length >= 3,
error: value.length < 3 ? 'Username must be at least 3 characters' : undefined
}),
password: (value) => ({
valid: value.length >= 8,
error: value.length < 8 ? 'Password must be at least 8 characters' : undefined
}),
rememberMe: () => ({ valid: true }) // 布尔值总是有效
};

// const loginData: LoginForm = { username: 'jo', password: '123', rememberMe: true };
// const validationResults = validateForm(loginData, loginValidators);
// 结果会被正确地类型化

TypeScript高级类型编程是一个深奥而强大的主题,需要通过大量的实践来掌握。在实际项目中,应该平衡类型安全和开发效率,避免过度复杂的类型操作导致编译性能下降或代码难以维护。

总结

  TypeScript的高级类型系统为开发者提供了强大的类型编程能力,通过巧妙运用条件类型、映射类型、分布式条件类型等特性,可以实现极其严格的类型安全,将潜在的运行时错误提前到编译时捕获。

  在实践中,应该根据项目需求选择适当的类型抽象级别,既要保证类型安全,又要避免过度工程化。TypeScript高级类型的主要价值在于:

  1. 编译时错误检测:尽可能在编译阶段发现类型错误
  2. API可用性改善:提供更准确的自动补全和类型提示
  3. 重构安全性:类型系统能够捕捉重构过程中的破坏性变更
  4. 文档化作用:类型定义本身就是一种形式化的API文档

  掌握这些高级类型技巧,将使你在TypeScript项目中编写出更安全、更可靠的代码。

bulb