架构视角:类型即文档

TypeScript不仅是一门编程语言,更是一种架构设计工具。优秀的类型系统能够在编译期捕获错误、提供智能提示、增强代码可读性,甚至指导软件架构设计。将类型视为不可执行的文档,是TypeScript进阶的关键思维转变。

TypeScript架构价值

  • 编译期安全:在代码运行前发现类型错误,减少运行时异常
  • 自文档化:类型定义本身就是最好的API文档
  • 重构友好:IDE支持全局重命名、查找引用、安全重构
  • 领域建模:使用类型系统表达业务领域模型
  • 团队协作:类型契约明确组件接口,降低沟通成本

高级类型系统

条件类型与类型推断

// 条件类型基础

// 基础条件类型
type IsString = T extends string ? true : false;

type A = IsString;  // true
type B = IsString;  // false

// 分布式条件类型
type ToArray = T extends any ? T[] : never;
type C = ToArray;  // string[] | number[]

// 非分布式条件类型(使用方括号包裹)
type ToArrayNonDist = [T] extends [any] ? T[] : never;
type D = ToArrayNonDist;  // (string | number)[]

// infer关键字 - 类型推断
// 提取函数返回类型
type ReturnType = T extends (...args: any[]) => infer R ? R : never;

// 提取Promise解析类型
type Awaited = T extends Promise ? U : T;

// 提取数组元素类型
type ElementType = T extends (infer E)[] ? E : never;

// 提取对象属性类型
type PropType = T extends { [P in K]: infer V } ? V : never;

// 实用类型工具
// 深度Partial
type DeepPartial = {
  [P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
};

// 深度Readonly
type DeepReadonly = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly : T[P];
};

// 提取可选属性
type OptionalKeys = {
  [K in keyof T]-?: {} extends Pick ? K : never;
}[keyof T];

// 提取必填属性
type RequiredKeys = {
  [K in keyof T]-?: T extends Record ? K : never;
}[keyof T];

// 扁平化对象类型
type Flatten = T extends object ? T[keyof T] : T;

模板字面量类型

// 模板字面量类型

// 基础用法
type EventName = `on${Capitalize}`;
type ClickEvent = EventName<'click'>;  // 'onClick'

// 组合多个类型
type HTTPMethod = 'get' | 'post' | 'put' | 'delete';
type Endpoint = '/users' | '/posts' | '/comments';
type APIPath = `${Uppercase} ${Endpoint}`;
// 'GET /users' | 'GET /posts' | ... | 'DELETE /comments'

// 对象属性路径类型
// 实现类似lodash.get的类型安全版本
type Path = K extends string
  ? T[K] extends Record
    ? `${K}` | `${K}.${Path}`
    : `${K}`
  : never;

interface User {
  name: string;
  address: {
    street: string;
    city: string;
    country: {
      code: string;
      name: string;
    };
  };
}

type UserPath = Path;
// 'name' | 'address' | 'address.street' | 'address.city' | 
// 'address.country' | 'address.country.code' | 'address.country.name'

// CSS变量类型
type CSSVariable = `--${T}`;
type ThemeColor = 'primary' | 'secondary' | 'success' | 'error';
type CSSCustomProperty = CSSVariable;
// '--primary' | '--secondary' | '--success' | '--error'

映射类型高级用法

// 映射类型进阶

// 重映射键名
type Getters = {
  [K in keyof T as `get${Capitalize}`]: () => T[K];
};

interface Person {
  name: string;
  age: number;
}

type PersonGetters = Getters;
// { getName: () => string; getAge: () => number; }

// 过滤属性
type FilterByValueType = {
  [K in keyof T as T[K] extends V ? K : never]: T[K];
};

type StringProps = FilterByValueType;
// { name: string; }

// 移除特定修饰符
type Mutable = {
  -readonly [K in keyof T]: T[K];
};

type Optional = {
  [K in keyof T]?: T[K];
};

type Required = {
  [K in keyof T]-?: T[K];
};

// 递归映射类型
type DeepMutable = {
  -readonly [K in keyof T]: T[K] extends object 
    ? DeepMutable 
    : T[K];
};

// 键名转换
type CamelCase = 
  S extends `${infer P}_${infer Q}`
    ? `${P}${Capitalize>}`
    : S;

type SnakeToCamel = {
  [K in keyof T as CamelCase]: T[K] extends object
    ? SnakeToCamel
    : T[K];
};

interface SnakeCaseData {
  user_name: string;
  created_at: string;
  user_address: {
    street_name: string;
    zip_code: string;
  };
}

type CamelCaseData = SnakeToCamel;
// {
//   userName: string;
//   createdAt: string;
//   userAddress: { streetName: string; zipCode: string; };
// }

类型体操实战

实现高级类型工具

// 类型体操挑战

// 1. 实现Tuple转Union
type TupleToUnion = T[number];
type Result1 = TupleToUnion<['a', 'b', 'c']>;  // 'a' | 'b' | 'c'

// 2. 实现Union转Intersection
type UnionToIntersection = 
  (U extends any ? (k: U) => void : never) extends (k: infer I) => void 
    ? I 
    : never;

type Result2 = UnionToIntersection<{ a: string } | { b: number }>;
// { a: string } & { b: number }

// 3. 实现PickByType
type PickByType = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface Example {
  name: string;
  age: number;
  isActive: boolean;
  createdAt: Date;
}

type StringProps = PickByType;  // { name: string; }

// 4. 实现DeepOmit
type DeepOmit = T extends object
  ? {
      [P in keyof T as P extends K ? never : P]: T[P] extends object
        ? DeepOmit
        : T[P];
    }
  : T;

// 5. 实现Curry类型
type Curry any> = T extends (
  arg: infer A,
  ...rest: infer R
) => infer Ret
  ? R extends []
    ? (arg: A) => Ret
    : (arg: A) => Curry<(...args: R) => Ret>
  : T;

function add(a: number, b: number, c: number): number {
  return a + b + c;
}

type CurriedAdd = Curry;
// (arg: number) => (arg: number) => (arg: number) => number

// 6. 实现IsNever
type IsNever = [T] extends [never] ? true : false;

// 7. 实现IsAny
type IsAny = 0 extends 1 & T ? true : false;

// 8. 实现Exact类型(精确匹配,不允许额外属性)
type Exact = T extends Shape
  ? Exclude extends never
    ? T
    : never
  : never;

// 9. 实现FlattenArray
type FlattenArray = T extends [infer F, ...infer R]
  ? F extends any[]
    ? [...FlattenArray, ...FlattenArray]
    : [F, ...FlattenArray]
  : [];

type Flattened = FlattenArray<[1, [2, 3], [[4]]]>;
// [1, 2, 3, 4]

// 10. 实现StringLength
type StringLength<
  S extends string,
  Acc extends any[] = []
> = S extends `${string}${infer Rest}`
  ? StringLength
  : Acc['length'];

type Len = StringLength<'hello'>;  // 5

类型安全的数据库查询

// 类型安全的数据库查询构建器

interface TableSchema {
  users: {
    id: number;
    name: string;
    email: string;
    createdAt: Date;
  };
  posts: {
    id: number;
    title: string;
    content: string;
    authorId: number;
    published: boolean;
  };
}

type TableName = keyof TableSchema;

type QueryBuilder = {}
> = {
  table: T;
  select: Select[];
  where?: Where;
  limit?: number;
  orderBy?: {
    column: keyof TableSchema[T];
    direction: 'asc' | 'desc';
  };
};

function createQuery(table: T) {
  return {
    select: (...columns: K[]) => ({
      where: (conditions: Partial) => ({
        build: (): QueryBuilder> => ({
          table,
          select: columns,
          where: conditions
        })
      })
    })
  };
}

// 使用
const query = createQuery('users')
  .select('id', 'name', 'email')
  .where({ name: 'John' })
  .build();

// query类型为:
// QueryBuilder<'users', 'id' | 'name' | 'email', { name: string }>

运行时类型安全

使用Zod进行Schema验证

// 运行时类型安全

import { z } from 'zod';

// 定义Schema
const UserSchema = z.object({
  id: z.number().int().positive(),
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).max(150).optional(),
  role: z.enum(['admin', 'user', 'guest']).default('user'),
  createdAt: z.date().default(() => new Date()),
  metadata: z.record(z.unknown()).optional(),
});

// 推断TypeScript类型
type User = z.infer;

// 验证数据
function parseUser(data: unknown): User {
  return UserSchema.parse(data);  // 验证失败抛出错误
}

function safeParseUser(data: unknown): User | null {
  const result = UserSchema.safeParse(data);
  return result.success ? result.data : null;
}

// 异步验证
const AsyncUserSchema = UserSchema.extend({
  avatar: z.string().url().refine(
    async (url) => {
      // 验证URL可访问
      const response = await fetch(url, { method: 'HEAD' });
      return response.ok;
    },
    { message: 'Avatar URL is not accessible' }
  ),
});

// 转换类型
const UserInputSchema = z.object({
  name: z.string(),
  email: z.string().email(),
  age: z.string().transform((val) => parseInt(val, 10)),
});

// 组合Schema
const AddressSchema = z.object({
  street: z.string(),
  city: z.string(),
  zipCode: z.string(),
});

const UserWithAddressSchema = UserSchema.extend({
  address: AddressSchema,
});

// 数组验证
const UserListSchema = z.array(UserSchema);

// 联合类型
const ResultSchema = z.union([
  z.object({ success: z.literal(true), data: UserSchema }),
  z.object({ success: z.literal(false), error: z.string() }),
]);

// API响应类型
const ApiResponseSchema = (dataSchema: T) =>
  z.object({
    status: z.number(),
    message: z.string(),
    data: dataSchema,
  });

const UserResponseSchema = ApiResponseSchema(UserSchema);
type UserResponse = z.infer;

branded types(品牌类型)

// 品牌类型:在结构类型系统中实现名义类型

declare const __brand: unique symbol;

type Brand = T & { [__brand]: B };

// 定义品牌类型
type UserId = Brand;
type PostId = Brand;
type Email = Brand;

// 创建品牌类型值
function createUserId(id: string): UserId {
  return id as UserId;
}

function createPostId(id: string): PostId {
  return id as PostId;
}

// 使用
const userId = createUserId('user-123');
const postId = createPostId('post-456');

// 类型错误!不能混用
function getUser(id: UserId): User { /* ... */ }
getUser(userId);  // OK
getUser(postId);  // Error: Argument of type 'PostId' is not assignable to parameter of type 'UserId'

// 验证品牌类型
function isEmail(value: string): value is Email {
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
}

function sendEmail(to: Email, subject: string) {
  // 确保to是验证过的Email类型
}

const email = 'test@example.com';
if (isEmail(email)) {
  sendEmail(email, 'Hello');  // OK
}

// 更安全的品牌类型模式
type Validated = Brand;

interface FormData {
  email: string;
  password: string;
}

function validateForm(data: FormData): Validated | null {
  if (data.email.includes('@') && data.password.length >= 8) {
    return data as Validated;
  }
  return null;
}

function submitForm(data: Validated) {
  // 只能提交验证过的数据
}

设计模式与架构

依赖注入类型安全

// 类型安全的依赖注入

// 服务令牌
type ServiceToken = symbol & { __type: T };

function createToken(name: string): ServiceToken {
  return Symbol(name) as ServiceToken;
}

// 容器接口
interface Container {
  register(token: ServiceToken, instance: T): void;
  resolve(token: ServiceToken): T;
}

// 实现
class ServiceContainer implements Container {
  private services = new Map();
  
  register(token: ServiceToken, instance: T): void {
    this.services.set(token, instance);
  }
  
  resolve(token: ServiceToken): T {
    const service = this.services.get(token);
    if (!service) {
      throw new Error(`Service not found: ${String(token)}`);
    }
    return service as T;
  }
}

// 定义服务
interface IUserService {
  getUser(id: string): Promise;
}

interface ILogger {
  log(message: string): void;
}

// 创建令牌
const UserServiceToken = createToken('UserService');
const LoggerToken = createToken('Logger');

// 使用
const container = new ServiceContainer();

container.register(LoggerToken, console);
container.register(UserServiceToken, new UserService());

// 类型安全的解析
const userService = container.resolve(UserServiceToken);
const logger = container.resolve(LoggerToken);

事件总线类型安全

// 类型安全的事件总线

// 事件映射
type EventMap = {
  'user:login': { userId: string; timestamp: Date };
  'user:logout': { userId: string };
  'post:created': { postId: string; authorId: string };
  'error': { message: string; code: number };
};

// 类型安全的事件总线
class TypedEventBus> {
  private listeners: {
    [K in keyof Events]?: Set<(payload: Events[K]) => void>;
  } = {};
  
  on(
    event: K,
    listener: (payload: Events[K]) => void
  ): () => void {
    if (!this.listeners[event]) {
      this.listeners[event] = new Set();
    }
    this.listeners[event]!.add(listener);
    
    return () => {
      this.listeners[event]!.delete(listener);
    };
  }
  
  emit(event: K, payload: Events[K]): void {
    this.listeners[event]?.forEach(listener => listener(payload));
  }
  
  once(
    event: K,
    listener: (payload: Events[K]) => void
  ): void {
    const onceListener = (payload: Events[K]) => {
      listener(payload);
      this.off(event, onceListener);
    };
    this.on(event, onceListener);
  }
  
  off(
    event: K,
    listener: (payload: Events[K]) => void
  ): void {
    this.listeners[event]?.delete(listener);
  }
}

// 使用
const eventBus = new TypedEventBus();

// 类型安全的事件监听
eventBus.on('user:login', ({ userId, timestamp }) => {
  console.log(`User ${userId} logged in at ${timestamp}`);
});

// 类型安全的事件触发
eventBus.emit('user:login', {
  userId: '123',
  timestamp: new Date(),
});

// 错误:类型不匹配
eventBus.emit('user:login', {
  userId: '123',
  // timestamp: 'now',  // Error: Type 'string' is not assignable to type 'Date'
});

状态机类型

// 类型安全的状态机

type StateMachineConfig = {
  initial: State;
  states: {
    [S in State]: {
      on?: {
        [E in Event]?: State | { target: State; action?: () => void };
      };
      entry?: () => void;
      exit?: () => void;
    };
  };
};

class StateMachine<
  State extends string,
  Event extends string
> {
  private currentState: State;
  
  constructor(private config: StateMachineConfig) {
    this.currentState = config.initial;
  }
  
  getState(): State {
    return this.currentState;
  }
  
  send(event: Event): void {
    const stateConfig = this.config.states[this.currentState];
    const transition = stateConfig.on?.[event];
    
    if (!transition) return;
    
    const targetState = typeof transition === 'string' 
      ? transition 
      : transition.target;
    
    // 执行exit动作
    stateConfig.exit?.();
    
    // 执行transition action
    if (typeof transition === 'object') {
      transition.action?.();
    }
    
    // 切换状态
    this.currentState = targetState;
    
    // 执行entry动作
    this.config.states[targetState].entry?.();
  }
}

// 定义状态机
type TrafficLightState = 'green' | 'yellow' | 'red';
type TrafficLightEvent = 'TIMER' | 'POWER_OUTAGE';

const trafficLightMachine = new StateMachine({
  initial: 'green',
  states: {
    green: {
      on: { TIMER: 'yellow' },
      entry: () => console.log('Green light!'),
    },
    yellow: {
      on: { TIMER: 'red' },
      entry: () => console.log('Yellow light!'),
    },
    red: {
      on: { TIMER: 'green' },
      entry: () => console.log('Red light!'),
    },
  },
});

// 使用
trafficLightMachine.send('TIMER');  // OK
trafficLightMachine.send('POWER_OUTAGE');  // OK(虽然没有定义,但类型允许)
// trafficLightMachine.send('UNKNOWN');  // Error: Argument of type '"UNKNOWN"' is not assignable

架构决策总结

场景 推荐方案 说明
运行时验证 Zod / Yup / io-ts API响应、表单数据验证
品牌类型 branded types ID区分、验证状态标记
配置类型 模板字面量类型 环境变量、路径类型
API类型 OpenAPI + 代码生成 前后端契约一致性
全局状态 类型安全的事件总线 模块间通信
复杂业务逻辑 类型安全状态机 工作流、订单状态

TypeScript最佳实践

  • 启用严格模式:strict: true捕获更多错误
  • 优先使用类型推断:减少显式类型注解
  • 使用unknown而非any:强制类型检查
  • 利用const断言:as const获取最精确类型
  • 类型与实现分离:类型定义在.d.ts文件
  • 避免过度类型体操:可读性优先

总结

TypeScript的类型系统是图灵完备的,能够表达极其复杂的类型关系。但类型的终极目的是服务于代码质量和开发体验,而非炫技。

掌握高级类型技巧后,应该将其应用于实际架构设计:构建类型安全的API层、设计领域模型、实现可靠的依赖注入。让类型成为团队的共同语言,提升整体开发效率和代码可靠性。