下面我有一个简单的例子,我有一个类B,它有一个泛型参数“T”I-我想限制某个方法参数的允许值,只允许那些与参数“T”的键相匹配的值。
在我的大脑里,我下面的东西应该做我认为我想要做的事情,但是我只是得到了我在那里的线上放的错误。
我想要的是,它将把调用类似“foo”方法的值限制为仅与T参数匹配的值,因此b.foo("asdf")
可以,但b.foo("qwerty")
不行(“qwerty”不在keyof A中)。
如果能提供任何帮助,我们将不胜感激。
interface A {
asdf: string;
}
type Names<X extends A> = X extends A ? keyof X : never;
class B<T extends A> {
foo(N: Names<T>) {
}
baz(n: Names<T>) {
this.foo("asdf"); // Argument of type '"asdf"' is not assignable to parameter of type 'Names<T>'.ts(2345)
this.foo("qwerty"); // expect an error here, but not the other ones
this.foo(n); // fine?
}
}
interface C extends A {
qwerty: string;
}
class D<T extends C> extends B<T> {
bar() {
this.foo("asdf"); // Argument of type '"asdf"' is not assignable to parameter of type 'Names<T>'.ts(2345)
this.foo("qwerty"); // Argument of type '"qwerty"' is not assignable to parameter of type 'Names<T>'.ts(2345)
this.baz("asdf"); // Argument of type '"asdf"' is not assignable to parameter of type 'Names<T>'.ts(2345)
this.baz("qwerty"); // Argument of type '"qwerty"' is not assignable to parameter of type 'Names<T>'.ts(2345)
}
}
1条答案
按热度按时间8ftvxx2r1#
在这里给出的例子中,你只需要直接使用
keyof
操作符,而不是定义Names
(或者你可以定义type Names<X extends A> = keyof X
):这将按照需要进行编译,没有任何错误。
另一方面,
Names<T>
的定义本质上与(除了
Names
的输入被约束为A
)。这是一个分布式条件类型,它将输入拆分成它的联合成员,获取每个成员的键,并将它们一起放回到联合中。因此,虽然keyof (A | B)
是来自每个联合成员的键的交集,等价于(keyof A) & (keyof B)
,类型AllKeysOf<A | B>
等价于联合体(keyof A) | (keyof B)
。根据您的用例,这种差异可能会很重要。但是依赖于generic类型参数的条件类型,比如
B
和D
类体中的Names<T>
,编译器实际上并不求值。它 * 延迟 * 它们的求值,因此它们是 * 不透明 * 类型。例如,在B<T>
的baz()
方法中,编译器不知道Names<T>
可能是什么类型,它会错误地拒绝所有类型。所以,虽然你和我可以推理出"asdf"
必须可赋值给Names<T>
,如果T extends A
,编译器就会放弃并抱怨,所以我建议如果你不需要使用泛型条件类型,就不要使用它们:如果keyof T
可以工作,那么您应该使用它来代替Names<T>
。Playground代码链接