TypeScript 编程 前端
TypeScript进阶技巧与模式
掌握TypeScript的高级特性和设计模式,提升代码质量和开发效率。从泛型到装饰器,从类型守卫到条件类型。
5分钟
592 字
TypeScript 进阶技巧与模式
TypeScript 作为 JavaScript 的超集,为我们提供了强大的类型系统。掌握其高级特性能够显著提升代码质量和开发体验。
高级类型系统
1. 条件类型 (Conditional Types)
条件类型允许我们根据类型条件来选择不同的类型:
typescript
type ApiResponse<T> = T extends string ? { message: T } : { data: T };
type StringResponse = ApiResponse<string>; // { message: string }
type NumberResponse = ApiResponse<number>; // { data: number }
2. 映射类型 (Mapped Types)
映射类型让我们能够基于现有类型创建新类型:
typescript
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
interface User {
id: number;
name: string;
email?: string;
}
type PartialUser = Partial<User>; // 所有属性都变为可选
type RequiredUser = Required<User>; // 所有属性都变为必需
3. 模板字面量类型
TypeScript 4.1 引入的强大特性:
typescript
type EventName<T extends string> = `on${Capitalize<T>}`;
type ButtonEvents = EventName<"click" | "hover">; // 'onClick' | 'onHover'
type Route<T extends string> = `/api/${T}`;
type UserRoutes = Route<"users" | "posts">; // '/api/users' | '/api/posts'
高级泛型技巧
1. 泛型约束
使用泛型约束来限制类型参数:
typescript
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// 正确:string有length属性
loggingIdentity("hello");
// 正确:数组有length属性
loggingIdentity([1, 2, 3]);
// 错误:number没有length属性
// loggingIdentity(123);
2. 键值约束
确保对象键的类型安全:
typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30, city: "New York" };
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
// const invalid = getProperty(person, "invalid"); // 编译错误
3. 条件类型与 infer
使用infer关键字提取类型信息:
typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type ArrayElement<T> = T extends (infer U)[] ? U : never;
function getString(): string {
return "hello";
}
function getNumbers(): number[] {
return [1, 2, 3];
}
type StringReturn = ReturnType<typeof getString>; // string
type NumberArray = ArrayElement<number[]>; // number
实用工具类型
1. 深度只读类型
typescript
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
interface Config {
database: {
host: string;
port: number;
};
cache: {
ttl: number;
};
}
type ReadonlyConfig = DeepReadonly<Config>;
// 所有嵌套属性都变为只读
2. 类型安全的事件发射器
typescript
interface EventMap {
"user:login": { userId: string; timestamp: Date };
"user:logout": { userId: string };
"data:update": { id: string; data: any };
}
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, listener: (data: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}
emit<K extends keyof T>(event: K, data: T[K]): void {
const eventListeners = this.listeners[event];
if (eventListeners) {
eventListeners.forEach((listener) => listener(data));
}
}
}
const emitter = new TypedEventEmitter<EventMap>();
// 类型安全的事件监听
emitter.on("user:login", (data) => {
console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});
// 类型安全的事件发射
emitter.emit("user:login", {
userId: "123",
timestamp: new Date(),
});
装饰器模式
1. 类装饰器
typescript
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class BugReport {
type = "report";
title: string;
constructor(t: string) {
this.title = t;
}
}
2. 方法装饰器
typescript
function measure(
target: any,
propertyName: string,
descriptor: PropertyDescriptor
) {
const method = descriptor.value;
descriptor.value = function (...args: any[]) {
const start = performance.now();
const result = method.apply(this, args);
const finish = performance.now();
console.log(`${propertyName} took ${finish - start} milliseconds`);
return result;
};
}
class Calculator {
@measure
fibonacci(n: number): number {
if (n < 2) return n;
return this.fibonacci(n - 1) + this.fibonacci(n - 2);
}
}
类型守卫
1. 用户定义的类型守卫
typescript
interface Bird {
fly(): void;
layEggs(): void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // TypeScript知道这里pet是Fish类型
} else {
pet.fly(); // TypeScript知道这里pet是Bird类型
}
}
2. 判别联合类型
typescript
interface LoadingState {
status: "loading";
}
interface SuccessState {
status: "success";
data: any;
}
interface ErrorState {
status: "error";
error: string;
}
type State = LoadingState | SuccessState | ErrorState;
function handleState(state: State) {
switch (state.status) {
case "loading":
// state的类型被缩窄为LoadingState
console.log("Loading...");
break;
case "success":
// state的类型被缩窄为SuccessState
console.log("Data:", state.data);
break;
case "error":
// state的类型被缩窄为ErrorState
console.log("Error:", state.error);
break;
}
}
模块声明与命名空间
1. 声明文件
为第三方库创建类型声明:
typescript
// types/my-library.d.ts
declare module "my-library" {
export interface Config {
apiKey: string;
timeout?: number;
}
export function initialize(config: Config): void;
export function getData<T>(endpoint: string): Promise<T>;
}
2. 全局类型扩展
扩展全局类型:
typescript
declare global {
interface Window {
myCustomProperty: string;
}
namespace NodeJS {
interface ProcessEnv {
NODE_ENV: "development" | "production" | "test";
API_URL: string;
}
}
}
// 现在可以安全使用
window.myCustomProperty = "value";
const apiUrl = process.env.API_URL;
最佳实践
1. 严格模式配置
json
{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noImplicitThis": true
}
}
2. 类型优先的开发方式
typescript
// 先定义类型
interface UserRepository {
findById(id: string): Promise<User | null>;
create(user: CreateUserDto): Promise<User>;
update(id: string, updates: Partial<User>): Promise<User>;
delete(id: string): Promise<void>;
}
// 再实现具体逻辑
class DatabaseUserRepository implements UserRepository {
async findById(id: string): Promise<User | null> {
// 实现逻辑
}
// ... 其他方法实现
}
总结
TypeScript 的高级特性为我们提供了强大的类型安全保障和开发体验。通过掌握这些技巧,我们可以:
- 编写更安全、更可维护的代码
- 提升开发效率和代码质量
- 减少运行时错误
- 改善团队协作
记住,类型系统是为了帮助我们,而不是阻碍我们。合理使用这些高级特性,能让 TypeScript 成为你最得力的开发伙伴。
继续探索 TypeScript 的世界,你会发现更多令人惊喜的特性和可能性。