TypeScript:访问泛型对象以生成类型化函数

TypeScript:访问泛型对象以生成类型化函数,typescript,typescript-typings,Typescript,Typescript Typings,我有一个提供解码功能的接口 interface Converter<T> { uuid: CUUID; decode: (value: T) => Buffer; encode?: (value: Buffer) => T; } 这样我就可以将此类对象传递给服务: interface MyConverters extends Converters { state: Converter<string>; water: Converter&l

我有一个提供
解码
功能的接口

interface Converter<T> {
  uuid: CUUID;
  decode: (value: T) => Buffer;
  encode?: (value: Buffer) => T;
}
这样我就可以将此类对象传递给
服务

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

const service = new Service<MyConverters>();
我希望
name
Converter
中的任意键,
value
decode
函数的
ReturnType
,相应的
Converter
就是
Converter[name]
(因此基本上是
Converter

这就是我最终得出的结论:

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 {}

}
我不知道发生了什么

最终,我想让这成为可能:

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
接口MyConverters扩展转换器{
状态:转换器;
水:转炉;
}
const service=新服务();
服务。写(“水”,12);
^      ^
检查这两种类型是否与MyConverter匹配

在开始之前,我们需要做几件事:

  • 类型转换器={name:转换器


    简言之,我放弃了
    扩展转换器
    ,转而支持条件推理。另外,我从
    转换器
    推断出
    T
    ,而不是
    返回类型
    ,看看
    返回类型
    在幕后是如何使用条件推理的。无论如何,这样做似乎更简单


    如果您试图传递一个不是转换器的键,那么无论您提供什么值,您的
    write
    调用都不会编译。

    因此,在开始之前,我们需要做几件事:

    • 类型转换器={name:转换器


      简言之,我放弃了
      扩展转换器
      ,转而支持条件推理。另外,我从
      转换器
      推断出
      T
      ,而不是
      返回类型
      ,看看
      返回类型
      在幕后是如何使用条件推理的。无论如何,这样做似乎更简单


      如果您试图传递一个不是转换器的键,那么无论您提供什么值,您的
      write
      调用都不会编译。

      您遇到的第一个问题是
      转换器的外观。这只是一个类型为
      转换器的
      名称
      键的类型

      编辑 @内河马达拉酒店♦ ConverterValue的版本比我的好,我会使用它

      type ConverterValue<C extends Converters<keyof C>, N extends keyof C> = C[N] extends Converter<infer R> ? R : never;
      
      
      type ConverterValue=C[N]扩展转换器?R:从不;
      
      您遇到的第一个问题是
      转换器的外观。这只是一个带有
      名称的类型
      键的类型
      转换器

      编辑 @内河马达拉酒店♦ ConverterValue的版本比我的好,我会使用它

      type ConverterValue<C extends Converters<keyof C>, N extends keyof C> = C[N] extends Converter<infer R> ? R : never;
      
      
      type ConverterValue=C[N]扩展转换器?R:从不;
      
      你确定要
      ReturnType
      ,因为它总是缓冲区。也许你的意思是
      encode
      ?很抱歉,这是一个复制粘贴错误,实际上参数和返回类型是相反的。如果你改变你的答案,我可以改变我的问题来清理这个线程。我认为这对大部分答案没有多大关系我所做的编辑使参数的提取以任何一种方式工作。你确定你想要
      ReturnType
      ,因为这将始终是缓冲区。也许你的意思是
      encode
      ?很抱歉,这是一个复制粘贴错误,实际上参数和返回类型是相反的。如果你更改答案,我可以更改我的问题以清除此线程不要认为这对大部分答案有多大影响。我所做的编辑使参数的提取无论哪种方式都有效。@MadaraUchiha它不是真正的递归,把
      keyof t
      看作是一个完全独立的类型,只是碰巧是
      t
      omg的键,你是上帝,这是有效的。我已经为此奋斗了很长时间了解决方案非常恰当。非常感谢你,伊玛接受了@MadaraUchiha的回答,因为它有点干净。不过,谢谢你!@Lukas你的电话,他的回答并不限制
      C
      成为任何东西,这可能是好事,也可能是坏事thing@MadaraUchiha它不是真正的递归,可以将
      keyof T
      看作是一个完全的sep阿拉特类型,恰好是
      T
      omg的钥匙,你是上帝,这是有效的。我已经为此奋斗了这么长时间,解决方案非常正确。非常感谢你,伊玛接受@MadaraUchiha的答案,因为它有点干净。不过,谢谢你!@Lukas你的电话,他的答案并不限制
      C
      ,T帽子可以是好东西也可以是坏东西
      C[K]扩展转换器?R:never
      绝对更好,惭愧我没想到好吧,我很敬畏,你们太棒了!
      C[K]扩展转换器?R:never
      绝对更好,惭愧我没想到好吧,我敬畏,你们太棒了!
      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
      
      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.
      
      interface Converter<T> {
        uuid: CUUID;
        decode: (value: T) => Buffer;
        encode?: (value: Buffer) => T;
      }
      
      
      type Converters<K extends PropertyKey> = Record<K, Converter<any>>
      
      
      type ConverterValue<C extends Converters<keyof C>, N extends keyof C> = Parameters<C[N]["decode"]>[0];
      
      class Service<C extends Converters<keyof C>> {
      
        public write<N extends keyof C>(
          name: N,
          value: ConverterValue<C, N>
        ): void {}
      
      }
      
      interface MyConverters extends Converters<keyof MyConverters> {
        state: Converter<string>;
        water: Converter<number>;
      }
      
      const service = new Service<MyConverters>();
      
      service.write("water", 12);
      service.write("water", "12"); // err
      service.write("state", "");
      
      
      type ConverterValue<C extends Converters<keyof C>, N extends keyof C> = C[N] extends Converter<infer R> ? R : never;