对于generics,您的版本不能很好地使用TypeScrip的推理算法,并且迫使您手动指定泛型类型参数,从而导致重复.理想情况下,您希望类型判断器推断您的类型,但从类型Omit<T, K>
的值推断T
基本上是行不通的(the Omit
utility type是根据conditional type实现的,而TypeScrip实际上不能对泛型条件类型进行太多分析).
相反,您应该考虑使推断尽可能简单,方法是要求编译器从类型为T
的值推断出T
.它看起来是这样的:
function insertSelf<T, K extends PropertyKey>(prop: K, o: T): ⋯
然后剩下的就是用T
和K
来描述返回类型了.这里有一种方法:
type Self<K extends PropertyKey, T> =
T & { [P in K]: Self<K, T> }
因此,Self<K, T>
是类型T
的值,它在键为K
的属性处也具有类型Self<K, T>
的属性.这是一种递归类型,描述的关系非常类似于您在Foo
中使用的polymorphic this
type.
实现方式可能是
function insertSelf<T, K extends PropertyKey>(prop: K, o: T): Self<K, T>;
function insertSelf(prop: PropertyKey, o: any) {
return o[prop] = o;
}
在这里,我只是使用单个调用签名overload来 suppress 来自编译器的任何错误,而不理解return o[prop]=o
如何工作或与Self<K, T>
相关.
让我们试试看:
const f = insertSelf('c', { a: 1, b: 2 });
// const f: Self<"c", { a: number; b: number; }>
console.log(f.a.toFixed(2)) // 1.00
console.log(f.c.c.c.b.toFixed(2)) // 2.00
看起来不错,f
的类型是Self<"c", {a: number, b: number}>
,因此它具有您期望的所有属性.
另请注意,这并不妨碍您使用包含this
属性类型的自定义接口:
interface Foo {
a: number,
b: number,
c: this
}
const foo: Foo = f; // okay
该赋值之所以成功,是因为编译器能够验证Self<"c", { a: number; b: number; }>
是否可赋值给Foo
.因此,如果你对Self
机型不满意,你可以 Select .
Playground link to code个