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

如何为链式重载的lazylet函数定义TypeScript类型?

为lazylet函数补充TypeScript类型定义

我们定义Dict类型为{ [key: string]: () => any },期望返回类型为X。现需为目标函数定义类型:

  • 该函数接收Dict类型参数T,返回X;
  • X本身也是函数,接收Dict类型参数U后返回X;
  • X可访问U的所有属性及T中未被U覆盖的属性;
  • 返回值仅允许访问已定义的键。

函数实现代码

export function lazylet(values) {
  const createStore = (overrides) => {
    return lazylet({
      ...values,
      ...overrides,
    });
  };

  Object.entries(values).map(([key, factory]) => {
    Object.defineProperty(createStore, key, {
      enumerable: true,
      configurable: true,
      get() {
        const value = factory()
        Object.defineProperty(createStore, key, { get: () => value });
        return value;
      },
    })
  });

  return createStore;
};

使用示例

const laz1 = lazylet({ a: () => "hello", b: () => Math.random() })
/* const laz1: LazyLet<{
    a: string;
    b: number;
}> */
console.log(laz1.a.toUpperCase()) // "HELLO"
console.log(laz1.b.toFixed(2)) // "0.67" 或其他随机值
console.log(laz1.b.toFixed(2)) // 与上次结果相同(已缓存)

const laz2 = laz1({ c: () => Math.random() < 0.5 });
/* const laz2: LazyLet<{
    a: string;
    b: number;
    c: boolean;
}> */
console.log(laz2.c) // true 或 false

console.log(laz2.d) // 报错!
// --> ~
// Property 'd' does not exist on type 'LazyLet<{ a: string; b: number; } & { c: boolean; }>'.

const laz3 = laz2({ b: () => "123", d: () => 456 })
/* const laz3: LazyLet<{
    a: string;
    c: boolean;
    b: string;
    d: number;
}> */

console.log(laz3.b) // "123"(覆盖了原有的number类型)
console.log(laz3.d) // 456

类型定义实现

通过泛型与交叉类型可以满足需求:

// 定义工厂字典类型:每个键对应返回对应类型的工厂函数
type FactoryDict<T> = { [K in keyof T]: () => T[K] };

// LazyLet核心类型:既是可调用的扩展函数,又是包含所有属性的对象
type LazyLet<T> = ((U: FactoryDict<U>) => LazyLet<Omit<T, keyof U> & U>) & T;

export function lazylet<T>(values: FactoryDict<T>): LazyLet<T> {
  const createStore = ((overrides: FactoryDict<any>) => {
    return lazylet({
      ...values,
      ...overrides,
    }) as any;
  }) as LazyLet<T>;

  Object.entries(values).map(([key, factory]) => {
    Object.defineProperty(createStore, key, {
      enumerable: true,
      configurable: true,
      get() {
        const value = factory()
        Object.defineProperty(createStore, key, { get: () => value });
        return value;
      },
    })
  });

  return createStore;
};

类型说明

  • FactoryDict<T>:将目标对象的属性类型转换为对应的工厂函数类型,匹配问题中定义的Dict;
  • LazyLet<T>:通过交叉类型实现双重特性——既可以作为函数接收新的工厂字典来扩展/覆盖属性,又可以直接访问已定义的所有属性;
  • 实现中的as any断言是合理的:TypeScript无法自动推导嵌套泛型的合并结果,手动断言可保证类型兼容性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 03:54:27