首页 > 解决方案 > TypeScript:访问泛型对象以生成类型化函数

问题描述

我有一个提供decode功能的界面

interface Converter<T> {
  uuid: CUUID;
  decode: (value: T) => Buffer;
  encode?: (value: Buffer) => T;
}

不,我有一个名为的类Service,它提供了一个包含任意数量的这些转换器的对象:

type Converters = { name: Converter<any> };

class Service<C extends Converters> {

}

这样我就可以将这些对象传递给Service

interface MyConverters extends Converters {
  state: Converter<string>;
  water: Converter<number>;
}

const service = new Service<MyConverters>();

该类Service有几个函数应该在传递给转换器Converters的 Generic T( ) 上工作。Converter<T>所以不要这样做:

class Service<C extends Converters> {

  public write(name: string, value: any): void {

  }

}

我希望name成为任何关键,Convertersvalue成为对应ReturnType的功能(所以基本上是of )。decodeConverterConverters[name]<T>Converter<T>

这就是我最终想出的:

interface Converter<T> {
  uuid: CUUID;
  decode: (value: T) => Buffer;
  encode?: (value: Buffer) => T;
}

type Converters = { name: Converter<any> };

type ConverterNames<C extends Converters> = keyof C;

type ConverterValue<C extends Converters, N extends Keys<C>> = ReturnType<C[N]["decode"]>;

class Service<C extends Converters> {

  public write<N extends ConverterNames<C>>(
    name: N,
    value: ConverterValue<C, N>
  ): void {}

}

…但它不起作用

type ConverterValue<C extends Converters, N extends ConverterNames<C>> = ReturnType<C[N]["decode"]>;
                                                                                   ^^^^^^^^^^^^^^

我收到错误

Type 'C[N]["decode"]' does not satisfy the constraint '(...args: any) => any'.
  Type 'C[keyof C]["decode"]' is not assignable to type '(...args: any) => any'.
    Type 'C[string]["decode"] | C[number]["decode"] | C[symbol]["decode"]' is not assignable to type '(...args: any) => any'.
      Type 'C[string]["decode"]' is not assignable to type '(...args: any) => any'.ts(2344)
Type '"decode"' cannot be used to index type 'C[N]'.ts(2536)

而且我不确定发生了什么。

最终我想让这成为可能:

interface MyConverters extends Converters {
  state: Converter<string>;
  water: Converter<number>;
}

const service = new Service<MyConverters>();

service.write("water", 12);
                 ^      ^
                          checks if these 2 types match MyConverters

标签: typescripttypescript-typings

解决方案


所以在我们开始之前有几件事:

  • type Converters = { name: Converter<any> };不是一个键映射到 aConverter<any>的对象,它是一个键映射name到a 的对象Converter<any>,我怀疑你想要类似的东西type Converters = { [name: string]: Converter<any>; },但这也行不通。见下文。
  • 我认为不可能将泛型限制为,{ [name: string]: Converter<any>; }因为任何扩展类型都需要提供索引签名(即允许访问任何字符串,这不是您想要的。

我来过的最好的是

interface Converter<T> {
    uuid: CUUID;
    decode: (value: T) => Buffer;
    encode?: (value: Buffer) => T;
}


class Service<C> {
    public write<K extends keyof C>(
        name: K,
        value: C[K] extends Converter<infer R> ? R : never
    ): void { }
}

interface MyConverters {
    state: Converter<string>;
    water: Converter<number>;
}

const service = new Service<MyConverters>();

service.write('water', 'foo'); // Error, expected number.

游乐场链接


简而言之,我放弃了extends Converters支持条件推理。另外,我在推断TfromConverter<T>而不是ReturnType<Converter<T>['decoder']>,看看如何ReturnType在幕后使用条件推断,这种方式似乎更简单。

如果您尝试传递不是 Converter 的密钥,write则无论您提供什么值,您的调用都不会编译。


推荐阅读