1704 字
9 分钟
TypeScript 进阶技巧:提升代码质量的实用指南

引言#

TypeScript 已成为现代前端开发的事实标准。掌握其高级特性不仅能提升代码质量,还能显著提高开发效率。本文将介绍一些实用的 TypeScript 进阶技巧。

1. 类型守卫与类型收窄#

自定义类型守卫#

类型守卫可以帮助 TypeScript 在运行时识别具体类型:

interface Cat {
type: 'cat';
meow: () => void;
}
interface Dog {
type: 'dog';
bark: () => void;
}
type Animal = Cat | Dog;
// 使用 type predicate
function isCat(animal: Animal): animal is Cat {
return animal.type === 'cat';
}
function handleAnimal(animal: Animal) {
if (isCat(animal)) {
animal.meow(); // TypeScript 知道这里是 Cat
} else {
animal.bark(); // TypeScript 知道这里是 Dog
}
}

使用 in 操作符#

interface User {
name: string;
email: string;
}
interface Admin {
name: string;
permissions: string[];
}
function printInfo(person: User | Admin) {
console.log(person.name);
if ('permissions' in person) {
console.log('Permissions:', person.permissions);
} else {
console.log('Email:', person.email);
}
}

2. 泛型进阶技巧#

泛型约束#

interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength('hello'); // ✅
logLength([1, 2, 3]); // ✅
logLength({ length: 10, value: 3 }); // ✅
// logLength(123); // ❌ number 没有 length 属性

泛型默认参数#

interface ApiResponse<T = unknown> {
data: T;
status: number;
message: string;
}
// 使用默认类型
const response: ApiResponse = {
data: null,
status: 200,
message: 'Success'
};
// 指定具体类型
const userResponse: ApiResponse<User> = {
data: { name: 'John', email: 'john@example.com' },
status: 200,
message: 'Success'
};

条件类型#

type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实用示例:提取 Promise 的返回类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type Result = Awaited<Promise<string>>; // string
type Direct = Awaited<number>; // number

3. 映射类型#

内置工具类型#

interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial - 所有属性变为可选
type PartialUser = Partial<User>;
// Required - 所有属性变为必需
type RequiredUser = Required<PartialUser>;
// Readonly - 所有属性变为只读
type ReadonlyUser = Readonly<User>;
// Pick - 选择指定属性
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit - 排除指定属性
type UserWithoutId = Omit<User, 'id'>;
// Record - 创建对象类型
type UserMap = Record<string, User>;

自定义映射类型#

// 将所有属性变为可为 null
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
type NullableUser = Nullable<User>;
// 将所有函数属性的返回类型提取出来
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never;
}[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;

4. 模板字面量类型#

字符串操作#

// 内置字符串操作类型
type Greeting = "hello world";
type UppercaseGreeting = Uppercase<Greeting>; // "HELLO WORLD"
type LowercaseGreeting = Lowercase<Greeting>; // "hello world"
type CapitalizedGreeting = Capitalize<Greeting>; // "Hello world"
type UncapitalizedGreeting = Uncapitalize<UppercaseGreeting>; // "hELLO WORLD"
// 构建路由类型
type Route = '/users' | '/products' | '/orders';
type APIRoute = `${Route}/${string}`;
const route1: APIRoute = '/users/123'; // ✅
const route2: APIRoute = '/products/abc'; // ✅
// const route3: APIRoute = '/invalid/123'; // ❌

类型安全的事件系统#

type EventMap = {
'user:login': { userId: string; timestamp: Date };
'user:logout': { userId: string };
'data:update': { id: number; data: unknown };
};
type EventName = keyof EventMap;
function emit<K extends EventName>(
event: K,
payload: EventMap[K]
): void {
console.log(`Event ${event}:`, payload);
}
emit('user:login', {
userId: '123',
timestamp: new Date()
}); // ✅
// emit('user:login', { userId: '123' }); // ❌ 缺少 timestamp
// emit('invalid:event', {}); // ❌ 不存在的事件

5. 索引访问类型#

interface User {
id: number;
profile: {
name: string;
age: number;
address: {
city: string;
country: string;
};
};
}
// 访问嵌套类型
type ProfileType = User['profile'];
// { name: string; age: number; address: { ... } }
type AddressType = User['profile']['address'];
// { city: string; country: string }
// 获取数组元素类型
type StringArray = string[];
type ArrayElement = StringArray[number]; // string
// 获取对象所有值的类型
type UserValues = User[keyof User];
// number | { name: string; age: number; ... }

6. infer 关键字#

提取函数参数类型#

type Parameters<T> = T extends (...args: infer P) => any ? P : never;
function greet(name: string, age: number) {
console.log(`Hello ${name}, age ${age}`);
}
type GreetParams = Parameters<typeof greet>; // [string, number]
// 提取第一个参数类型
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any
? F
: never;
type FirstParam = FirstParameter<typeof greet>; // string

提取 Promise 类型#

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
async function fetchUser(): Promise<User> {
return { id: 1, name: 'John', email: 'john@example.com', age: 30 };
}
type FetchUserResult = UnwrapPromise<ReturnType<typeof fetchUser>>; // User

7. 类型断言与类型收窄#

双重断言#

// 有时需要先断言为 unknown,再断言为目标类型
const value = 'hello' as unknown as number; // 不推荐,但有时必要
// 更好的方式:使用类型守卫
function isNumber(value: unknown): value is number {
return typeof value === 'number';
}

非空断言#

// 使用 ! 告诉 TypeScript 值不为 null/undefined
function getValue(map: Map<string, string>, key: string) {
const value = map.get(key);
return value!.toUpperCase(); // 确保 value 存在时使用
}
// 更安全的方式
function getValueSafe(map: Map<string, string>, key: string) {
const value = map.get(key);
if (!value) {
throw new Error(`Key ${key} not found`);
}
return value.toUpperCase();
}

8. 类型编程实战#

深度只读#

type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
interface Config {
database: {
host: string;
port: number;
credentials: {
username: string;
password: string;
};
};
}
type ReadonlyConfig = DeepReadonly<Config>;
// 所有嵌套属性都变为只读

深度可选#

type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object
? DeepPartial<T[P]>
: T[P];
};
type PartialConfig = DeepPartial<Config>;
// 所有嵌套属性都变为可选

路径字符串类型#

type PathKeys<T> = T extends object
? {
[K in keyof T]: K extends string
? T[K] extends object
? K | `${K}.${PathKeys<T[K]>}`
: K
: never;
}[keyof T]
: never;
type UserPaths = PathKeys<User>;
// "id" | "profile" | "profile.name" | "profile.age" | ...

9. 装饰器类型#

// 类装饰器
function Logger<T extends { new (...args: any[]): {} }>(constructor: T) {
return class extends constructor {
createdAt = new Date();
};
}
@Logger
class MyClass {
name = 'MyClass';
}
// 方法装饰器
function Log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`Calling ${propertyKey} with`, args);
return original.apply(this, args);
};
return descriptor;
}
class Calculator {
@Log
add(a: number, b: number) {
return a + b;
}
}

10. 性能优化技巧#

避免过度计算#

// ❌ 每次访问都重新计算
type BadExample<T> = {
[K in keyof T]: T[K] extends string ? Uppercase<T[K]> : T[K];
};
// ✅ 使用类型别名缓存结果
type ToUppercase<S> = S extends string ? Uppercase<S> : S;
type GoodExample<T> = {
[K in keyof T]: ToUppercase<T[K]>;
};

使用接口而非类型别名#

// 对于对象类型,接口通常性能更好
interface User {
id: number;
name: string;
}
// 类型别名适用于联合类型、交叉类型等
type ID = string | number;
type Nullable<T> = T | null;

最佳实践总结#

  1. 优先使用类型推断:让 TypeScript 自动推断类型,减少手动标注
  2. 使用严格模式:启用 strict: true 捕获更多潜在问题
  3. 避免 any:使用 unknown 替代 any,保持类型安全
  4. 合理使用工具类型:善用内置工具类型简化代码
  5. 文档化复杂类型:为复杂的类型添加注释说明
  6. 渐进式采用:在现有项目中逐步引入 TypeScript

结语#

TypeScript 的类型系统非常强大,掌握这些进阶技巧可以帮助我们编写更安全、更易维护的代码。记住,类型系统的目的是帮助我们,而不是束缚我们。在实际开发中,要在类型安全和开发效率之间找到平衡点。


推荐资源:

TypeScript 进阶技巧:提升代码质量的实用指南
https://www.qiandulab.com/posts/typescript-advanced-tips/
作者
Agent实验室
发布于
2025-11-07
许可协议
CC BY-NC-SA 4.0