typescript 如何根据泛型参数的键限制参数值?

6pp0gazn  于 2023-03-09  发布在  TypeScript
关注(0)|答案(1)|浏览(233)

下面我有一个简单的例子,我有一个类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)
    }
}
8ftvxx2r

8ftvxx2r1#

在这里给出的例子中,你只需要直接使用keyof操作符,而不是定义Names(或者你可以定义type Names<X extends A> = keyof X):

interface A {
  asdf: string;
}

class B<T extends A> {
  foo(N: keyof T) {

  }

  baz(n: keyof T) {
    this.foo("asdf"); // ok
    this.foo("qwerty"); // err
    this.foo(n); // ok
  }
}

interface C extends A {
  qwerty: string;
}

class D<T extends C> extends B<T> {
  bar() {
    this.foo("asdf"); // ok
    this.foo("qwerty"); // ok
    this.baz("asdf"); // ok
    this.baz("qwerty"); // ok
    this.baz("blork"); // error
  }
}

这将按照需要进行编译,没有任何错误。
另一方面,Names<T>的定义本质上与

type AllKeysOf<T> = T extends unknown ? keyof T : never;

(除了Names的输入被约束为A)。这是一个分布式条件类型,它将输入拆分成它的联合成员,获取每个成员的键,并将它们一起放回到联合中。因此,虽然keyof (A | B)是来自每个联合成员的键的交集,等价于(keyof A) & (keyof B),类型AllKeysOf<A | B>等价于联合体(keyof A) | (keyof B)。根据您的用例,这种差异可能会很重要。
但是依赖于generic类型参数的条件类型,比如BD类体中的Names<T>,编译器实际上并不求值。它 * 延迟 * 它们的求值,因此它们是 * 不透明 * 类型。例如,在B<T>baz()方法中,编译器不知道Names<T>可能是什么类型,它会错误地拒绝所有类型。所以,虽然你和我可以推理出"asdf"必须可赋值给Names<T>,如果T extends A,编译器就会放弃并抱怨,所以我建议如果你不需要使用泛型条件类型,就不要使用它们:如果keyof T可以工作,那么您应该使用它来代替Names<T>
Playground代码链接

相关问题