在方法中使用 keyof 键入参数

In method use keyof to type parameter

假设您有以下 class:

class Foo {
  protected callbacks: Callbacks = {};

  bar(key: string) {
     const callback = this.callbacks[key];
     // do stuff with callback
  }

  ...
}

回调 属性 可以通过其他方法更改(稍后)。

如何确保作为属性提供的键是回调的实际键 属性? 如果将回调实例传递给函数,我会这样做:

function bar<T, Key extends keyof T>(callbacks:T, key: Key) { ... }

但这不适用于 'this' 关键字。我知道在泛型中使用原始 this.callbacks 是不正确的,因为 this.callbacks 不是类型,而是实例。然而,用 typeof 转换它也不会产生结果..

class Foo {
  protected callbacks: Callbacks = {};

  bar<Key extends keyof typeof this.callbacks>(key: Key) { // Cannot find name 'this'
     const callback = this.callbacks[key];
     // do stuff with callback
  }

  ...
}

我是不是遗漏了什么明显的东西?我的整个设计模式有缺陷吗?

要使这种模式可行,您需要 class 提前知道其 callbacks 属性 的确切键。我不知道 Callbacks 应该是什么类型,但它显然不够具体,无法满足您的要求?否则,您只需将 bar() 方法的 key 参数注释为 key: keyof Callbacks.

假设没有特定的 Callbacks 类型适合您,而是您想使用不同的 callbacks 键创建不同的 Foo 实例,您应该使 Foo 成为 generic class,其中泛型类型参数(称之为 K)对应于该实例的 callbacks 属性 的键的并集:

class Foo<K extends PropertyKey> {

  constructor(protected callbacks: { [P in K]: () => void }) { }

  bar(key: K) {
    const callback = this.callbacks[key];
    callback();
  }

}

(在没有更多信息的情况下,我假设 callbacks 的所有属性都可以用零参数调用。)因为 callbacks 属性 具有函数值属性在 K 中的键处,那么 bar() 方法的 key 参数可以安全地为 K。让我们来测试一下:

const foo = new Foo({
  sayHello() { console.log("HELLO!") },
  sayGoodbye() { console.log("GOODBYE!") }
})

foo.bar("sayHello"); // HELLO!
foo.bar("sayGoodbye"); // GOODBYE!

创建 foo 后,由于传入的回调对象,编译器将其推断为 Foo<"sayHello" | "sayGoodbye"> 类型。因此 foo.bar() 只能用 [=32 调用=] 或 "sayGoodbye" 作为其参数。

Playground link to code