我使用此代码将ChildClass类型的属性动态分配给ParentClass
export type KeyOfValueType<T, U> = {
[V in keyof T]: T[V] extends U ? V : never
}[keyof T];
class ChildClass {
public name: string;
}
class ParentClass {
public child: ChildClass;
}
class Setter {
public propName: KeyOfValueType<ParentClass, ChildClass>;
public setProp(parent: ParentClass, child: ChildClass) {
parent[this.propName] = child; // <== No Error
}
}
如果我尝试创建“通用”设置器,则会收到错误消息:
class GenericSetter<T, U> {
public propName: KeyOfValueType<T, U>; // No Error
public setProp(parent: T, child: U) {
parent[this.propName] = child; // <== Error U is not assignable to T[{[V in keyof T]: T[V] extends U ? V : never}]
}
}
class Setter extends GenericSetter<ParentClass, ChildClass> {
public setProp(parent: ParentClass, child: ChildClass) {
parent[this.propName] = child; // <== No Error with Parent propName
}
}
答案 0 :(得分:1)
从根本上讲,这是design limitation, see microsoft/TypeScript#30728。对于依赖于泛型类型参数的条件类型,编译器不会做太多可分配性分析。也许可以对此进行改进,但是会在类型检查器复杂性和编译时间方面付出一些代价,并且只有某些专门从事此类工作的用户才能看到任何好处。所以我不指望它会发生。
有两种一般的处理方法:一种是尝试找到另一种表达类型约束的方式,以使编译器 可以验证您的代码,另一种方法是使用{ {3}}告诉编译器不要担心,您正在保证所做工作的安全性。在后一种情况下,您应该非常小心,首先要真正进行的工作是安全的。
值得注意的是,您的通用设置员代码不是安全的:
const oops = new GenericSetter<{ a: string }, string | number>()
oops.propName = "a";
const myParent = { a: "bar" };
oops.setProp(myParent, 123);
console.log(myParent.a.toUpperCase()); // no compiler error, but at runtime:
// TypeError: myParent.a.toUpperCase is not a function
您的KeyOfValueType<T, U>
的走错方法:它为您提供了T
的密钥,可以从中安全地读取类型为U
的值,但是您需要安全地将T
类型的值写到{em> 的U
键。因此,您需要类似的东西:
type KeyOfValueTypeForWriting<T, V> = {
[K in keyof T]: [V] extends [T[K]] ? K : never
}[keyof T];
class FixedGenericSetter<T, U> {
public propName!: KeyOfValueTypeForWriting<T, U>;
public setProp(parent: T, child: U) {
parent[this.propName] = child; // same error as before
}
}
const okay = new FixedGenericSetter<{ a: string }, string | number>()
okay.propName = "a"; // error here now
现在至少您只能将propName
设置为可安全写入的密钥,并且okay.propName = "a"
是一个错误,因为您不能安全地将string | number
写入a
{a: string}
的属性。
尽管如此,setProp()
内部的可分配性错误仍然存在,所以让我们尝试用我前面提到的两种方法来修复它。最简单,破坏最少的修复是类型声明:
class FixedGenericSetterAsserted<T, U> {
public propName!: KeyOfValueTypeForWriting<T, U>;
public setProp(parent: T, child: U) {
parent[this.propName] = child as any as T[KeyOfValueTypeForWriting<T, U>]; // assert
}
}
可以快速清除错误,但要对您负责安全。或者,您可以重构类型,以便不存在无法解析的条件类型,例如:
class FixedGenericSetterRefactored<K extends PropertyKey, U> {
public propName!: K;
public setProp(parent: Record<K, U>, child: U) {
parent[this.propName] = child;
}
}
const fine = new FixedGenericSetterRefactored<"a", string>()
fine.propName = "a";
fine.setProp({ a: "okay" }, "works");
这也可以正常工作,但是您的T
通用类型参数已消失,并被K
的相关键T
所代替。这样做比较安全,但是您可能还有其他用例,为什么需要指定T
而不是K
(尽管类型推断通常可以缓解这种情况)。
任何一种方法都可以为您工作。好的,希望能有所帮助;祝你好运!