如何为链式重载的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
相关产品推荐
相关产品推荐

