如何让TypeScript泛型自动适配listener类型实现参数补全?
问题描述
希望泛型T自动匹配listener字段的类型,无论listener是默认值还是自定义值,firstParameter都能获得自动补全,无需手动指定泛型类型。当前只能固定使用一种类型(dummy或upper),无法同时兼容多种,请问是否可行?如果不可行,除了将所有类型设为any外,还有其他解决办法吗?
代码示例
const upper = ({ helloWorld }: { helloWorld: string }) => helloWorld.toUpperCase(); const dummy = ({ worldHello }: { worldHello: string }) => worldHello; type objType<T extends (...args: any[]) => any = typeof upper> = { listener?: T; firstParameter: Parameters<T>[0]; }; const obj: objType = { listener: upper, firstParameter: { helloWorld: "hello world", // 自动补全正常 }, }; // 期望实现的数组场景 let arr: objType[] = [ obj, { listener: dummy, firstParameter: { worldHello: "world hello" } }, ]; // 期望无需手动指定泛型就能获得自动补全 let dummyObj: objType = { listener: dummy, firstParameter: { // 这里想要自动补全,不用写 objType<typeof dummy> } }
解决方案
核心结论
直接通过变量类型注解让泛型T根据listener属性反向推导是不可行的,因为TypeScript中变量的泛型类型必须显式指定或使用默认值,无法根据对象内部属性动态推导泛型参数。
替代方案1:用工厂函数创建对象(推荐)
利用函数的泛型推导特性,写一个工厂函数来生成目标对象,函数会自动根据传入的listener推导T的类型,无需手动指定:
const upper = ({ helloWorld }: { helloWorld: string }) => helloWorld.toUpperCase(); const dummy = ({ worldHello }: { worldHello: string }) => worldHello; type objType<T extends (...args: any[]) => any = typeof upper> = { listener?: T; firstParameter: Parameters<T>[0]; }; // 工厂函数,自动推导泛型T const createObj = <T extends (...args: any[]) => any = typeof upper>(obj: objType<T>) => obj; // 使用示例 const obj = createObj({ listener: upper, firstParameter: { helloWorld: "hello world" }, // 自动补全 }); const dummyObj = createObj({ listener: dummy, firstParameter: { worldHello: "world hello" }, // 自动补全 }); // 数组场景:使用联合类型数组 let arr: Array<objType<typeof upper> | objType<typeof dummy>> = [ obj, dummyObj, ];
替代方案2:定义联合类型(适合固定数量的listener场景)
如果listener的类型是固定的几种,可以直接把objType定义为所有可能类型的联合类型,这样数组和单个对象都能获得正确的类型提示:
const upper = ({ helloWorld }: { helloWorld: string }) => helloWorld.toUpperCase(); const dummy = ({ worldHello }: { worldHello: string }) => worldHello; type objType<T extends (...args: any[]) => any = typeof upper> = { listener?: T; firstParameter: Parameters<T>[0]; }; type UpperObj = objType<typeof upper>; type DummyObj = objType<typeof dummy>; type AnyObj = UpperObj | DummyObj; let arr: AnyObj[] = [ { listener: upper, firstParameter: { helloWorld: "hello world" } }, { listener: dummy, firstParameter: { worldHello: "world hello" } }, ]; let dummyObj: AnyObj = { listener: dummy, firstParameter: { worldHello: "world hello" }, // 自动补全 };
方案对比
- 工厂函数方案:扩展性强,新增
listener类型无需修改类型定义,推荐用于动态场景。 - 联合类型方案:类型提示更直接,但新增
listener时需要手动更新联合类型,适合固定场景。
内容的提问来源于stack exchange,提问作者Ronkzinho
相关产品推荐
相关产品推荐

