关于 typescript-advanced-types
面向 TypeScript 开发者的进阶类型系统指南,涵盖类型推断、可复用类型模式与测试,适用于构建类型安全的应用。
主要功能
- 01泛型与条件类型
- 02映射类型与模板字面量类型
- 03工具类型与递归类型
- 04类型推断与类型守卫
- 05类型测试与实践建议
适用场景
- 01构建类型安全的 API 客户端
- 02创建可复用的泛型组件
- 03设计表单验证类型
- 04将 JavaScript 代码迁移到 TypeScript
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ rolling-scopes/ rsschool-app --skill typescript-advanced-types安装后在 Claude Code 中输入/typescript-advanced-types确认已加载。
详细介绍
TypeScript 高级类型
全面介绍如何精通 TypeScript 的高级类型系统,包括泛型、条件类型、映射类型、模板字面量类型和工具类型,助你构建健壮且类型安全的应用。
何时使用此技能
- 构建类型安全的库或框架
- 创建可复用的泛型组件
- 实现复杂的类型推断逻辑
- 设计类型安全的 API 客户端
- 构建表单验证系统
- 创建强类型配置对象
- 实现类型安全的状态管理
- 将 JavaScript 代码库迁移到 TypeScript
核心概念
1. 泛型
用途: 在保持类型安全的同时,创建可复用且类型灵活的组件。
基本泛型函数:
function identity<T>(value: T): T {
return value;
}
const num = identity<number>(42); // Type: number
const str = identity<string>('hello'); // Type: string
const auto = identity(true); // Type inferred: boolean
泛型约束:
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): T {
console.log(item.length);
return item;
}
logLength('hello'); // OK: string has length
logLength([1, 2, 3]); // OK: array has length
logLength({ length: 10 }); // OK: object has length
// logLength(42); // Error: number has no length
多个类型参数:
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const merged = merge({ name: 'John' }, { age: 30 });
// Type: { name: string } & { age: number }
2. 条件类型
用途: 创建取决于条件的类型,以实现复杂的类型逻辑。
基本条件类型:
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
提取返回类型:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function getUser() {
return { id: 1, name: 'John' };
}
type User = ReturnType<typeof getUser>;
// Type: { id: number; name: string; }
分布式条件类型:
type ToArray<T> = T extends any ? T[] : never;
type StrOrNumArray = ToArray<string | number>;
// Type: string[] | number[]
嵌套条件:
type TypeName<T> = T extends string
? 'string'
: T extends number
? 'number'
: T extends boolean
? 'boolean'
: T extends undefined
? 'undefined'
: T extends Function
? 'function'
: 'object';
type T1 = TypeName<string>; // "string"
type T2 = TypeName<() => void>; // "function"
3. 映射类型
用途: 遍历现有类型的属性并对其进行转换。
基本映射类型:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface User {
id: number;
name: string;
}
type ReadonlyUser = Readonly<User>;
// Type: { readonly id: number; readonly name: string; }
可选属性:
type Partial<T> = {
[P in keyof T]?: T[P];
};
type PartialUser = Partial<User>;
// Type: { id?: number; name?: string; }
键重映射:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// Type: { getName: () => string; getAge: () => number; }
筛选属性:
type PickByType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
interface Mixed {
id: number;
name: string;
age: number;
active: boolean;
}
type OnlyNumbers = PickByType<Mixed, number>;
// Type: { id: number; age: number; }
4. 模板字面量类型
用途: 创建基于字符串的类型,并支持模式匹配和转换。
基本模板字面量:
type EventName = 'click' | 'focus' | 'blur';
type EventHandler = `on${Capitalize<EventName>}`;
// Type: "onClick" | "onFocus" | "onBlur"
字符串操作:
type UppercaseGreeting = Uppercase<'hello'>; // "HELLO"
type LowercaseGreeting = Lowercase<'HELLO'>; // "hello"
type CapitalizedName = Capitalize<'john'>; // "John"
type UncapitalizedName = Uncapitalize<'John'>; // "john"
路径构建:
type Path<T> = T extends object
? {
[K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never;
}[keyof T]
: never;
interface Config {
server: {
host: string;
port: number;
};
database: {
url: string;
};
}
type ConfigPath = Path<Config>;
// Type: "server" | "database" | "server.host" | "server.port" | "database.url"
5. 工具类型
内置工具类型:
// Partial<T> - Make all properties optional
type PartialUser = Partial<User>;
// Required<T> - Make all properties required
type RequiredUser = Required<PartialUser>;
// Readonly<T> - Make all properties readonly
type ReadonlyUser = Readonly<User>;
// Pick<T, K> - Select specific properties
type UserName = Pick<User, 'name' | 'email'>;
// Omit<T, K> - Remove specific properties
type UserWithoutPassword = Omit<User, 'password'>;
// Exclude<T, U> - Exclude types from union
type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // "b" | "c"
// Extract<T, U> - Extract types from union
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // "a" | "b"
// NonNullable<T> - Exclude null and undefined
type T3 = NonNullable<string | null | undefined>; // string
// Record<K, T> - Create object type with keys K and values T
type PageInfo = Record<'home' | 'about', { title: string }>;
高级模式
模式 1:类型安全的事件发射器
type EventMap = {
'user:created': { id: string; name: string };
'user:updated': { id: string };
'user:deleted': { id: string };
};
class TypedEventEmitter<T extends Record<string, any>> {
private listeners: {
[K in keyof T]?: Array<(data: T[K]) => void>;
} = {};
on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(callback);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
const callbacks = this.listeners[event];
if (callbacks) {
callbacks.forEach(callback => callback(data));
}
}
}
const emitter = new TypedEventEmitter<EventMap>();
emitter.on('user:created', data => {
console.log(data.id, data.name); // Type-safe!
});
emitter.emit('user:created', { id: '1', name: 'John' });
// emitter.emit("user:created", { id: "1" }); // Error: missing 'name'
模式 2:类型安全的 API 客户端
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type EndpointConfig = {
'/users': {
GET: { response: User[] };
POST: { body: { name: string; email: string }; response: User };
};
'/users/:id': {
GET: { params: { id: string }; response: User };
PUT: { params: { id: string }; body: Partial<User>; response: User };
DELETE: { params: { id: string }; response: void };
};
};
type ExtractParams<T> = T extends { params: infer P } ? P : never;
type ExtractBody<T> = T extends { body: infer B } ? B : never;
type ExtractResponse<T> = T extends { response: infer R } ? R : never;
class APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {
async request<Path extends keyof Config, Method extends keyof Config[Path]>(
path: Path,
method: Method,
...[options]: ExtractParams<Config[Path][Method]> extends never
? ExtractBody<Config[Path][Method]> extends never
? []
: [{ body: ExtractBody<Config[Path][Method]> }]
: [
{
params: ExtractParams<Config[Path][Method]>;
body?: ExtractBody<Config[Path][Method]>;
},
]
): Promise<ExtractResponse<Config[Path][Method]>> {
// Implementation here
return {} as any;
}
}
const api = new APIClient<EndpointConfig>();
// Type-safe API calls
const users = await api.request('/users', 'GET');
// Type: User[]
const newUser = await api.request('/users', 'POST', {
body: { name: 'John', email: '[email protected]' },
});
// Type: User
const user = await api.request('/users/:id', 'GET', {
params: { id: '123' },
});
// Type: User
模式 3:具有类型安全性的构建器模式
type BuilderState<T> = {
[K in keyof T]: T[K] | undefined;
};
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];
type IsComplete<T, S> = RequiredKeys<T> extends keyof S ? (S[RequiredKeys<T>] extends undefined ? false : true) : false;
class Builder<T, S extends BuilderState<T> = {}> {
private state: S = {} as S;
set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {
this.state[key] = value;
return this as any;
}
build(this: IsComplete<T, S> extends true ? this : never): T {
return this.state as T;
}
}
interface User {
id: string;
name: string;
email: string;
age?: number;
}
const builder = new Builder<User>();
const user = builder.set('id', '1').set('name', 'John').set('email', '[email protected]').build(); // OK: all required fields set
// const incomplete = builder
// .set("id", "1")
// .build(); // Error: missing required fields
模式 4:深层只读/部分类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? (T[P] extends Function ? T[P] : DeepReadonly<T[P]>) : T[P];
};
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? T[P] extends Array<infer U>
? Array<DeepPartial<U>>
: DeepPartial<T[P]>
: T[P];
};
interface Config {
server: {
host: string;
port: number;
ssl: {
enabled: boolean;
cert: string;
};
};
database: {
url: string;
pool: {
min: number;
max: number;
};
};
}
type ReadonlyConfig = DeepReadonly<Config>;
// All nested properties are readonly
type PartialConfig = DeepPartial<Config>;
// All nested properties are optional
模式 5:类型安全的表单验证
type ValidationRule<T> = {
validate: (value: T) => boolean;
message: string;
};
type FieldValidation<T> = {
[K in keyof T]?: ValidationRule<T[K]>[];
};
type ValidationErrors<T> = {
[K in keyof T]?: string[];
};
class FormValidator<T extends Record<string, any>> {
constructor(private rules: FieldValidation<T>) {}
validate(data: T): ValidationErrors<T> | null {
const errors: ValidationErrors<T> = {};
let hasErrors = false;
for (const key in this.rules) {
const fieldRules = this.rules[key];
const value = data[key];
if (fieldRules) {
const fieldErrors: string[] = [];
for (const rule of fieldRules) {
if (!rule.validate(value)) {
fieldErrors.push(rule.message);
}
}
if (fieldErrors.length > 0) {
errors[key] = fieldErrors;
hasErrors = true;
}
}
}
return hasErrors ? errors : null;
}
}
interface LoginForm {
email: string;
password: string;
}
const validator = new FormValidator<LoginForm>({
email: [
{
validate: v => v.includes('@'),
message: 'Email must contain @',
},
{
validate: v => v.length > 0,
message: 'Email is required',
},
],
password: [
{
validate: v => v.length >= 8,
message: 'Password must be at least 8 characters',
},
],
});
const errors = validator.validate({
email: 'invalid',
password: 'short',
});
// Type: { email?: string[]; password?: string[]; } | null
模式 6:可辨识联合
type Success<T> = {
status: 'success';
data: T;
};
type Error = {
status: 'error';
error: string;
};
type Loading = {
status: 'loading';
};
type AsyncState<T> = Success<T> | Error | Loading;
function handleState<T>(state: AsyncState<T>): void {
switch (state.status) {
case 'success':
console.log(state.data); // Type: T
break;
case 'error':
console.log(state.error); // Type: string
break;
case 'loading':
console.log('Loading...');
break;
}
}
// Type-safe state machine
type State =
| { type: 'idle' }
| { type: 'fetching'; requestId: string }
| { type: 'success'; data: any }
| { type: 'error'; error: Error };
type Event =
| { type: 'FETCH'; requestId: string }
| { type: 'SUCCESS'; data: any }
| { type: 'ERROR'; error: Error }
| { type: 'RESET' };
function reducer(state: State, event: Event): State {
switch (state.type) {
case 'idle':
return event.type === 'FETCH' ? { type: 'fetching', requestId: event.requestId } : state;
case 'fetching':
if (event.type === 'SUCCESS') {
return { type: 'success', data: event.data };
}
if (event.type === 'ERROR') {
return { type: 'error', error: event.error };
}
return state;
case 'success':
case 'error':
return event.type === 'RESET' ? { type: 'idle' } : state;
}
}
类型推断技巧
1. Infer 关键字
// Extract array element type
type ElementType<T> = T extends (infer U)[] ? U : never;
type NumArray = number[];
type Num = ElementType<NumArray>; // number
// Extract promise type
type PromiseType<T> = T extends Promise<infer U> ? U : never;
type AsyncNum = PromiseType<Promise<number>>; // number
// Extract function parameters
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
function foo(a: string, b: number) {}
type FooParams = Parameters<typeof foo>; // [string, number]
2. 类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string';
}
function isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[] {
return Array.isArray(value) && value.every(guard);
}
const data: unknown = ['a', 'b', 'c'];
if (isArrayOf(data, isString)) {
data.forEach(s => s.toUpperCase()); // Type: string[]
}
3. 断言函数
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
throw new Error('Not a string');
}
}
function processValue(value: unknown) {
assertIsString(value);
// value is now typed as string
console.log(value.toUpperCase());
}
最佳实践
- 优先使用
unknown,而非any:强制进行类型检查 - 对象结构优先使用
interface:错误消息更清晰 - 联合类型和复杂类型使用
type:灵活性更高 - 充分利用类型推断:尽可能让 TypeScript 自动推断
- 创建辅助类型:构建可复用的类型工具
- 使用 const 断言:保留字面量类型
- 避免类型断言:改用类型守卫
- 为复杂类型添加文档:添加 JSDoc 注释
- 使用严格模式:启用所有严格编译器选项
- 测试类型:使用类型测试验证类型行为
类型测试
// Type assertion tests
type AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;
type Test1 = AssertEqual<string, string>; // true
type Test2 = AssertEqual<string, number>; // false
type Test3 = AssertEqual<string | number, string>; // false
// Expect error helper
type ExpectError<T extends never> = T;
// Example usage
type ShouldError = ExpectError<AssertEqual<string, number>>;
常见陷阱
- 过度使用
any:违背 TypeScript 的初衷 - 忽略严格空值检查:可能导致运行时错误
- 类型过于复杂:可能拖慢编译速度
- 未使用可辨识联合:错失类型收窄的机会
- 忘记 readonly 修饰符:可能导致意外修改
- 循环类型引用:可能引发编译器错误
- 未处理边缘情况:例如空数组或 null 值
性能注意事项
- 避免深层嵌套的条件类型
- 尽可能使用简单类型
- 缓存复杂类型计算
- 限制递归类型的递归深度
- 使用构建工具在生产环境中跳过类型检查
资源
- TypeScript 手册:https://www.typescriptlang.org/docs/handbook/
- Type Challenges:https://github.com/type-challenges/type-challenges
- TypeScript Deep Dive:https://basarat.gitbook.io/typescript/
- Effective TypeScript:Dan Vanderkam 著
评论
支持 Markdown