如何避免TypeScript自引用接口构造时重复指定属性名?
TypeScript 自引用接口实例构造的泛型优化方案
问题背景
构造带有自引用属性的TypeScript接口实例时,手动编写自引用逻辑繁琐。现有insertSelf辅助函数需要重复指定属性名的字面量类型和字符串值,希望优化写法以避免重复,同时保留属性名和其他字段的编译检查能力。
优化方案1:利用TypeScript自动泛型推导
无需手动指定泛型参数,TypeScript会根据传入的属性名和对象自动推导T和K,彻底避免重复书写属性名:
function insertSelf<T, K extends keyof T>(prop: K, o: Omit<T, K>): T { return (o as any)[prop] = o as T; } interface Foo { a: number; b: number; c: this; } // 自动推导泛型,无需重复指定'c' const f: Foo = insertSelf('c', { a: 1, b: 2 }); // f.c 正确指向自身
编译检查能力保留
- 若传入不存在的属性名,会触发编译错误:
// 错误:'d'不是Foo的属性 const f2: Foo = insertSelf('d', { a: 1, b: 2 }); - 若传入的对象缺少必填字段,同样会触发编译错误:
// 错误:缺少b属性 const f3: Foo = insertSelf('c', { a: 1 });
优化方案2:柯里化函数(适合重复使用场景)
如果需要多次为同一个接口的同一属性构造实例,可以提前固定属性名,生成专用的构造函数,进一步简化调用:
function makeInsertSelf<K extends PropertyKey>(prop: K) { return function <T extends Record<K, T>>(o: Omit<T, K>): T { return (o as any)[prop] = o as T; }; } // 生成针对Foo接口c属性的专用构造函数 const insertFooC = makeInsertSelf('c'); // 后续调用无需再指定属性名 const f: Foo = insertFooC({ a: 1, b: 2 }); const f4: Foo = insertFooC({ a: 3, b: 4 });
这个方案同样保留完整的类型检查,且比原bind方案更符合TypeScript的类型系统设计。
内容的提问来源于stack exchange,提问作者Nathan Williams
相关产品推荐
相关产品推荐

