0

假设您有以下课程:

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
  }

  ...
}

我错过了一些明显的东西吗?我的整个设计模式有缺陷吗?

4

1 回答 1

1

为了使这种模式可能起作用,您需要您的班级提前知道其callbacks属性的确切键。我不知道Callbacks应该是什么类型,但是对于您要执行的操作显然不够具体?否则,您只需将方法的key参数注释为.bar()key: keyof Callbacks

假设没有特定Callbacks的类型适合你,而是你想Foo用不同的键创建不同的实例callbacks,你应该创建Foo一个泛型类,其中泛型类型参数(调用它)对应于该实例的属性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()只能使用"sayHello""sayGoodbye"作为其参数调用。

Playground 代码链接

于 2021-02-16T02:38:34.913 回答