AI时代的框架设计(四):TypeScript —— AI最喜欢的语言
返回文章列表

AI时代的框架设计(四):TypeScript —— AI最喜欢的语言

#为什么是TypeScript

上周和一个前端同事聊天,他说:"我们项目要不要迁移到TypeScript?"

我问:"为什么想迁移?"

他说:"感觉AI对我们的JavaScript代码理解不太好。比如问它某个函数怎么用,它总是猜错参数类型。"

这就对了。TypeScript不是为AI设计的,但恰好是AI最容易理解的语言。

原因很简单:

  • JavaScript:AI需要"猜"参数是什么类型
  • TypeScript:类型直接写在代码里
JavaScript
// JavaScript - AI需要猜
export function calculate(price, discount, isMember) {
  return isMember ? price * (1 - discount) : price;
}

// TypeScript - AI直接知道
export function calculate(
  price: number,
  discount: number,
  isMember: boolean
): number {
  return isMember ? price * (1 - discount) : price;
}

第二个版本,AI(和人类)一看就知道:

  • price 是数字
  • discount 是数字(折扣率)
  • isMember 是布尔值
  • 返回值是数字

这篇文章不是TypeScript教程,而是从AI可理解性的角度,讨论哪些TypeScript特性最有价值。

#TypeScript对AI的核心价值

#1. 静态类型 = 活文档

TypeScript
// ❌ JavaScript - 需要读文档
export function createUser(data) {
  // data是什么?有哪些字段?哪些必需?
}

// ✅ TypeScript - 类型即文档
export interface CreateUserData {
  username: string;
  email: string;
  password: string;
  age?: number;  // 可选
}

export function createUser(data: CreateUserData): User {
  // AI知道:
  // - data必须有username, email, password
  // - age是可选的
  // - 返回User对象
}

#2. IDE和AI能自动补全

TypeScript
interface User {
  id: string;
  username: string;
  email: string;
  profile: {
    firstName: string;
    lastName: string;
    avatar?: string;
  };
}

const user: User = getUser();

// 当你输入 user. 时,IDE和AI都知道有哪些字段
user.username  // ✓
user.email     // ✓
user.profile.firstName  // ✓
user.age       // ✗ 编译错误,没有这个字段

#3. 约束错误的使用方式

TypeScript
// 定义明确的选项类型
type DateFormat = 'ISO' | 'US' | 'EU' | 'report';

export function formatDate(date: Date, format: DateFormat): string {
  // ...
}

// 正确使用
formatDate(new Date(), 'ISO');     // ✓

// AI会提示错误
formatDate(new Date(), 'invalid'); // ✗ 类型错误
formatDate('2025-01-03', 'ISO');   // ✗ 第一个参数应该是Date

#4. 函数重载表达不同行为

TypeScript
// 清楚表达:传入ID返回单个用户,传入ID数组返回用户列表
export function getUser(id: string): Promise<User>;
export function getUser(ids: string[]): Promise<User[]>;
export function getUser(idOrIds: string | string[]): Promise<User | User[]> {
  if (Array.isArray(idOrIds)) {
    return fetchUsers(idOrIds);
  }
  return fetchUser(idOrIds);
}

// AI知道:
getUser('123')            // → Promise<User>
getUser(['123', '456'])   // → Promise<User[]>

#AI最喜欢的TypeScript特性

#特性1:明确的接口定义

TypeScript
/**
 * 应用配置
 */
export interface AppConfig {
  /** 应用名称 */
  name: string;
  
  /** 服务器端口 @default 3000 */
  port?: number;
  
  /** 运行环境 @default 'development' */
  env?: 'development' | 'production' | 'test';
  
  /** 数据库配置 */
  database: DatabaseConfig;
  
  /** 插件列表 */
  plugins?: Plugin[];
}

export interface DatabaseConfig {
  /** 数据库主机地址 */
  host: string;
  
  /** 数据库端口 @default 5432 */
  port?: number;
  
  /** 数据库名称 */
  database: string;
  
  /** 用户名 */
  username: string;
  
  /** 密码 */
  password: string;
}

AI从这些接口能理解:

  • 有哪些配置项
  • 哪些是必需的,哪些是可选的
  • 每个字段的用途
  • 默认值是什么

#特性2:类型别名和联合类型

TypeScript
// 明确的状态类型
export type OrderStatus = 
  | 'pending'      // 待处理
  | 'processing'   // 处理中
  | 'shipped'      // 已发货
  | 'delivered'    // 已送达
  | 'cancelled';   // 已取消

export interface Order {
  id: string;
  status: OrderStatus;  // 只能是上面5个值之一
  items: OrderItem[];
}

// AI知道:
order.status = 'pending';    // ✓
order.status = 'invalid';    // ✗ 编译错误
TypeScript
// 区分不同的ID类型
export type UserId = string & { readonly __brand: 'UserId' };
export type OrderId = string & { readonly __brand: 'OrderId' };
export type ProductId = string & { readonly __brand: 'ProductId' };

// 防止混淆不同类型的ID
export function getUser(id: UserId): User { }
export function getOrder(id: OrderId): Order { }

// AI会检查:
getUser(userId);      // ✓
getUser(orderId);     // ✗ 类型不匹配

#特性3:泛型表达通用模式

TypeScript
/**
 * API响应的通用格式
 */
export interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: {
    code: string;
    message: string;
  };
}

// 具体使用
export async function getUser(id: string): Promise<ApiResponse<User>> {
  // ...
}

export async function getOrders(): Promise<ApiResponse<Order[]>> {
  // ...
}

// AI理解:
const userResponse = await getUser('123');
// userResponse.data 的类型是 User | undefined

const ordersResponse = await getOrders();
// ordersResponse.data 的类型是 Order[] | undefined
TypeScript
/**
 * 分页响应
 */
export interface PaginatedResponse<T> {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
  hasMore: boolean;
}

export async function getUsers(
  page: number
): Promise<PaginatedResponse<User>> {
  // ...
}

// AI知道返回的结构
const response = await getUsers(1);
response.items      // User[]
response.total      // number
response.hasMore    // boolean

#特性4:工具类型表达变换

TypeScript
export interface User {
  id: string;
  username: string;
  email: string;
  password: string;
  createdAt: Date;
}

// 创建用户时,不需要id和createdAt
export type CreateUserInput = Omit<User, 'id' | 'createdAt'>;

// 更新用户时,所有字段都是可选的
export type UpdateUserInput = Partial<Omit<User, 'id' | 'createdAt'>>;

// 公开的用户信息,不包含密码
export type PublicUser = Omit<User, 'password'>;

// AI理解:
function createUser(input: CreateUserInput): User { }
// input需要:username, email, password

function updateUser(id: string, input: UpdateUserInput): User { }
// input可以有:username?, email?, password?

function getPublicProfile(id: string): PublicUser { }
// 返回的用户对象没有password字段

#特性5:字面量类型和判别联合

TypeScript
/**
 * 不同类型的事件
 */
export type AppEvent = 
  | { type: 'user.login'; userId: string; timestamp: Date }
  | { type: 'user.logout'; userId: string; timestamp: Date }
  | { type: 'order.created'; orderId: string; amount: number }
  | { type: 'order.cancelled'; orderId: string; reason: string };

export function handleEvent(event: AppEvent): void {
  // TypeScript能根据type字段判断具体类型
  switch (event.type) {
    case 'user.login':
      // 这里TypeScript知道event有userId和timestamp
      console.log('User logged in:', event.userId);
      break;
    
    case 'order.created':
      // 这里TypeScript知道event有orderId和amount
      console.log('Order created:', event.orderId, event.amount);
      break;
    
    // ...
  }
}

// AI能理解每种事件的具体结构

#特性6:条件类型推断

TypeScript
/**
 * 根据输入类型推断返回类型
 */
export function processData<T>(
  data: T,
  options: { 
    async: true 
  }
): Promise<T>;

export function processData<T>(
  data: T,
  options?: { 
    async?: false 
  }
): T;

export function processData<T>(
  data: T,
  options?: { async?: boolean }
): T | Promise<T> {
  if (options?.async) {
    return Promise.resolve(data);
  }
  return data;
}

// AI知道:
const result1 = processData(data, { async: true });
// result1 是 Promise<Data>

const result2 = processData(data);
// result2 是 Data

#实战:打造AI友好的类型系统

假设我们要设计一个数据验证框架的类型系统。

#第一版:基础类型

TypeScript
/**
 * 类型定义接口
 */
export interface TypeDefinition<T> {
  /** 类型名称 */
  name: string;
  
  /** 解析输入值 */
  parse: (input: unknown) => T;
  
  /** 验证值是否合法 */
  validate: (value: unknown) => value is T;
  
  /** 可选:默认值 */
  default?: T | (() => T);
  
  /** 可选:错误信息 */
  errorMessage?: string;
}

/**
 * 类型实例
 */
export interface Type<T> extends TypeDefinition<T> {
  /** 原始定义 */
  readonly definition: TypeDefinition<T>;
}

/**
 * 定义类型
 */
export function defineType<T>(
  definition: TypeDefinition<T>
): Type<T> {
  return {
    ...definition,
    definition
  };
}

#第二版:添加链式API

TypeScript
/**
 * 可链式调用的类型
 */
export interface ChainableType<T> extends Type<T> {
  /** 标记为可选 */
  optional(): ChainableType<T | undefined>;
  
  /** 设置默认值 */
  default(value: T | (() => T)): ChainableType<T>;
  
  /** 添加自定义验证 */
  refine(
    validate: (value: T) => boolean,
    message?: string
  ): ChainableType<T>;
}

/**
 * 字符串类型的扩展方法
 */
export interface StringType extends ChainableType<string> {
  /** 最小长度 */
  min(length: number): StringType;
  
  /** 最大长度 */
  max(length: number): StringType;
  
  /** 正则匹配 */
  pattern(regex: RegExp): StringType;
  
  /** 转小写 */
  toLowerCase(): StringType;
  
  /** 去除空格 */
  trim(): StringType;
}

/**
 * 数字类型的扩展方法
 */
export interface NumberType extends ChainableType<number> {
  /** 最小值 */
  min(value: number): NumberType;
  
  /** 最大值 */
  max(value: number): NumberType;
  
  /** 必须是整数 */
  integer(): NumberType;
  
  /** 必须是正数 */
  positive(): NumberType;
}

// AI能理解链式调用的类型推断
const username: StringType = StringType
  .min(3)
  .max(20)
  .trim()
  .toLowerCase();

const age: NumberType = NumberType
  .min(18)
  .max(120)
  .integer();

#第三版:Schema和类型推断

TypeScript
/**
 * Schema定义
 */
export type SchemaDefinition = {
  [key: string]: Type<any>;
};

/**
 * 从Schema推断类型
 */
export type InferSchema<S extends SchemaDefinition> = {
  [K in keyof S]: S[K] extends Type<infer T> ? T : never;
};

/**
 * Schema类
 */
export class Schema<T extends SchemaDefinition> {
  constructor(private definition: T) {}
  
  /**
   * 解析数据
   */
  parse(data: unknown): InferSchema<T> {
    // 实现...
  }
  
  /**
   * 验证数据
   */
  validate(data: unknown): data is InferSchema<T> {
    // 实现...
  }
}

/**
 * 创建Schema
 */
export function createSchema<T extends SchemaDefinition>(
  definition: T
): Schema<T> {
  return new Schema(definition);
}

// 使用示例
const userSchema = createSchema({
  username: StringType.min(3).max(20),
  email: EmailType,
  age: NumberType.min(18).optional()
});

// TypeScript自动推断类型!
const user = userSchema.parse(data);
// user的类型是:
// {
//   username: string;
//   email: string;
//   age?: number;
// }

// AI完全理解这个类型推断
user.username  // string
user.email     // string
user.age       // number | undefined

#第四版:嵌套和组合

TypeScript
/**
 * 数组类型
 */
export interface ArrayType<T> extends ChainableType<T[]> {
  /** 最小长度 */
  min(length: number): ArrayType<T>;
  
  /** 最大长度 */
  max(length: number): ArrayType<T>;
  
  /** 非空数组 */
  nonEmpty(): ArrayType<T>;
}

/**
 * 创建数组类型
 */
export function arrayOf<T>(itemType: Type<T>): ArrayType<T> {
  // 实现...
}

/**
 * 对象类型(嵌套Schema)
 */
export function objectOf<T extends SchemaDefinition>(
  schema: T
): ChainableType<InferSchema<T>> {
  // 实现...
}

// 复杂的嵌套结构
const postSchema = createSchema({
  id: StringType,
  title: StringType.min(1).max(200),
  content: StringType,
  author: objectOf({
    id: StringType,
    username: StringType,
    email: EmailType
  }),
  tags: arrayOf(StringType).min(1).max(5),
  comments: arrayOf(objectOf({
    id: StringType,
    content: StringType,
    authorId: StringType,
    createdAt: DateType
  })).optional()
});

// TypeScript推断出完整的嵌套类型
type Post = InferSchema<typeof postSchema>;
// {
//   id: string;
//   title: string;
//   content: string;
//   author: {
//     id: string;
//     username: string;
//     email: string;
//   };
//   tags: string[];
//   comments?: {
//     id: string;
//     content: string;
//     authorId: string;
//     createdAt: Date;
//   }[];
// }

// AI能理解整个嵌套结构
const post = postSchema.parse(data);
post.author.username     // string
post.tags[0]             // string
post.comments?.[0].content  // string | undefined

#TypeScript陷阱:避免过度复杂

并不是所有TypeScript特性都对AI友好。有些高级特性反而会让代码难以理解。

#陷阱1:过度使用高级类型

TypeScript
// ❌ 太复杂,AI难以理解
export type DeepPartial<T> = T extends object
  ? { [P in keyof T]?: DeepPartial<T[P]> }
  : T;

export type Prettify<T> = {
  [K in keyof T]: T[K];
} & {};

export type UnionToIntersection<U> = 
  (U extends any ? (k: U) => void : never) extends 
  (k: infer I) => void ? I : never;

// ✅ 简单清晰
export type UpdateUserInput = {
  username?: string;
  email?: string;
  profile?: {
    firstName?: string;
    lastName?: string;
  };
};

#陷阱2:隐式的类型转换

TypeScript
// ❌ 类型转换不明显
export function processData(data: any): any {
  return data as ProcessedData;
}

// ✅ 明确的类型和验证
export function processData(data: RawData): ProcessedData {
  if (!isValidRawData(data)) {
    throw new TypeError('Invalid raw data');
  }
  return transformToProcessedData(data);
}

#陷阱3:滥用泛型

TypeScript
// ❌ 泛型过多,难以理解
export function transform<T, U, V, W>(
  input: T,
  mapper1: (t: T) => U,
  mapper2: (u: U) => V,
  mapper3: (v: V) => W
): W {
  return mapper3(mapper2(mapper1(input)));
}

// ✅ 简化或拆分
export function transform<T, U>(
  input: T,
  mapper: (t: T) => U
): U {
  return mapper(input);
}

// 需要多步转换时,显式调用
const result = transform(
  transform(
    transform(input, step1),
    step2
  ),
  step3
);

#实用配置:tsconfig.json

JSON
{
  "compilerOptions": {
    // 基础配置
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020"],
    "moduleResolution": "bundler",
    
    // 严格模式(重要!)
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    
    // 生成声明文件
    "declaration": true,
    "declarationMap": true,
    "emitDeclarationOnly": false,
    
    // Source maps
    "sourceMap": true,
    
    // 输出配置
    "outDir": "./dist",
    "rootDir": "./src",
    
    // 路径配置
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    },
    
    // 其他
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}

关键配置解释:

TypeScript
// "strict": true 包含了:

// 1. noImplicitAny - 必须明确类型
function process(data) { }  // ✗ 错误
function process(data: UserData) { }  // ✓

// 2. strictNullChecks - 必须处理null/undefined
const user = findUser(id);
console.log(user.name);  // ✗ 错误:user可能是null
if (user) {
  console.log(user.name);  // ✓
}

// 3. strictFunctionTypes - 严格的函数类型检查
// 4. strictPropertyInitialization - 类属性必须初始化

// "noUncheckedIndexedAccess": true
const config: Record<string, string> = getConfig();
const value = config['key'];
// value的类型是 string | undefined(不是string)
// 强制你处理可能不存在的情况

#类型文档化的最佳实践

TypeScript
/**
 * 用户对象
 * 
 * 表示系统中的一个用户账户
 */
export interface User {
  /**
   * 用户唯一标识符
   * 格式:UUID v4
   */
  id: string;
  
  /**
   * 用户名
   * 限制:3-20个字符,只能包含字母、数字、下划线
   */
  username: string;
  
  /**
   * 邮箱地址
   * 必须是有效的邮箱格式
   */
  email: string;
  
  /**
   * 用户资料
   * 可能为空(新用户还未完善资料)
   */
  profile?: UserProfile;
  
  /**
   * 账户创建时间
   */
  createdAt: Date;
  
  /**
   * 最后活跃时间
   * 每次登录或重要操作时更新
   */
  lastActiveAt: Date;
}

/**
 * 创建用户的输入数据
 * 
 * @example
 * ```typescript
 * const input: CreateUserInput = {
 *   username: 'alice',
 *   email: 'alice@example.com',
 *   password: 'SecurePass123'
 * };
 * const user = await createUser(input);
 * ```
 */
export interface CreateUserInput {
  /** 用户名(3-20字符) */
  username: string;
  
  /** 邮箱地址 */
  email: string;
  
  /** 密码(至少8字符) */
  password: string;
  
  /** 可选:用户资料 */
  profile?: Partial<UserProfile>;
}

#小结

TypeScript是AI时代的理想语言,不是因为它是为AI设计的,而是因为:

  • 类型即文档
  • 编译时检查错误
  • IDE和AI都能理解
  • 强制清晰的接口设计

AI最喜欢的TypeScript特性:

  1. 接口定义 - 清晰的数据结构
  2. 联合类型 - 明确的选项集合
  3. 泛型 - 表达通用模式
  4. 工具类型 - 类型变换
  5. 判别联合 - 类型收窄
  6. 类型推断 - 自动推导

避免的陷阱:

  • 过度复杂的类型体操
  • 隐式的类型转换
  • 滥用any
  • 泛型参数过多

实用建议:

  • 开启strict模式
  • 所有公开API都要有类型
  • 用JSDoc补充类型无法表达的信息
  • 保持类型定义简单清晰

#系列总结

这个系列我们讨论了如何设计AI时代的框架和工具包:

第一篇:为什么和核心原则

  • 显式优于隐式
  • 语义化命名
  • 记录"为什么"
  • 代码即文档
  • 保持一致性

第二篇:代码组织

  • 文件大小:100-300行
  • 适度模块化
  • 按功能分组
  • 清晰的导出结构

第三篇:文档设计

  • JSDoc是AI的主要信息源
  • 记录"为什么",不只是"做什么"
  • 提供多个示例
  • 发布文档到npm
  • 使用工具自动检查

第四篇:TypeScript

  • 类型即文档
  • 接口和联合类型
  • 泛型和类型推断
  • 避免过度复杂
  • 开启strict模式

核心思想

AI友好的代码,本质上就是:

  • 清晰的
  • 明确的
  • 文档完善的
  • 易于理解的

这不仅对AI有益,对人类开发者也同样重要。在AI时代,好的代码设计会获得双重回报:更容易被人理解,也更容易被AI理解。

行动建议

从现在开始:

  1. 新项目用TypeScript
  2. 给所有导出函数加JSDoc
  3. 用ESLint检查注释完整性
  4. 文档和示例放进npm包
  5. 定期用AI测试你的代码是否易懂

最后的话

AI不会取代程序员,但会用AI的程序员会取代不会用AI的程序员。

而会用AI的第一步,就是写出AI能看懂的代码。