You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升TypeScript中Lens辅助构造函数的泛型索引类型安全性?

TypeScript Lens 类型安全优化方案

1. forProperty 无需重复传参的类型安全实现

核心思路是让属性名与投影类型自动关联,通过将属性名作为约束为 keyof S 的泛型参数,让 TypeScript 自动推导投影类型为该属性对应的类型,彻底避免手动指定泛型时的不匹配和重复传参问题:

class GenericLens<S, P> {
  constructor(
    public readonly get: (source: S) => P,
    public readonly set: (source: S, value: P) => S
  ) {}

  // 优化后的 forProperty
  static forProperty<S, K extends keyof S>(prop: K): GenericLens<S, S[K]> {
    return new GenericLens(
      (source) => source[prop],
      (source, value) => ({ ...source, [prop]: value })
    );
  }
}

用法说明

  • 无需手动指定投影类型 P,TypeScript 会根据传入的属性名自动推导:
    interface User { name: string; age: number; }
    
    // 自动推导投影类型为 string
    const nameLens = GenericLens.forProperty<User>('name');
    // 传入非法属性名会直接触发类型错误
    const invalidLens = GenericLens.forProperty<User>('invalidProp'); // 类型报错
    
  • 彻底解决了属性名与投影类型不匹配的问题,也不需要重复传递属性名相关的泛型参数。

2. forProperties 类型安全优化(匹配长度、顺序、类型)

利用 TypeScript 的元组类型映射特性,将属性名数组的每个元素约束为 S 的键,并自动推导每个位置对应的属性类型,确保属性名数组与投影元组的长度、顺序、类型完全一一对应:

class GenericLens<S, P> {
  constructor(
    public readonly get: (source: S) => P,
    public readonly set: (source: S, value: P) => S
  ) {}

  // 优化后的 forProperties
  static forProperties<S, K extends readonly (keyof S)[]>(
    ...props: K
  ): GenericLens<S, { [I in keyof K]: S[K[I]] }> {
    return new GenericLens(
      (source) => props.map(prop => source[prop]) as { [I in keyof K]: S[K[I]] },
      (source, values) => {
        const updated = { ...source };
        props.forEach((prop, idx) => {
          updated[prop] = values[idx];
        });
        return updated;
      }
    );
  }
}

用法说明

  • 属性名数组的长度、顺序、每个元素的合法性都会被 TypeScript 严格校验:
    interface User { name: string; age: number; email: string; }
    
    // 自动推导投影类型为 [string, number]
    const nameAgeLens = GenericLens.forProperties<User>('name', 'age');
    const user = { name: 'Alice', age: 30, email: 'alice@example.com' };
    const [name, age] = nameAgeLens.get(user); // name 为 string,age 为 number
    
    // 传入非法属性名触发类型错误
    const invalidPropsLens = GenericLens.forProperties<User>('name', 'invalidProp'); // 类型报错
    // 修改属性顺序后,投影元组的类型会自动同步
    const ageNameLens = GenericLens.forProperties<User>('age', 'name'); // 投影类型为 [number, string]
    
  • 完全解决了属性名数组与泛型元组不匹配的问题,无需手动指定元组类型,所有类型推导由 TypeScript 自动完成。

内容的提问来源于stack exchange,提问作者MBauerDC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 12:45:17