打字稿构造函数泛型参数推断

Typescript constructor generic argument inference

我正在尝试键入嵌套的嵌套序列化程序层次结构。下面的这个片段是我们现有内容的简化版本:

'use strict';

abstract class TypedSerializer<In, Out> {

  record: In;

  constructor(record: In) {
    this.record = record;
  }

  serializeObject<PIn, POut, S extends ObjectSerializerConstructor<PIn, POut>>(ctor: S, data: PIn): POut {
    const serializerInstance = new ctor(data);
    return serializerInstance.toObject();
  }

  serializeArray<PIn, POut, S extends ObjectSerializerConstructor<PIn, POut>>(ctor: S, arr: PIn[]): POut[] {
    const fn: ((PIn) => POut) = data => this.serializeObject(ctor, data);
    return arr.map(fn);
  }
}

type ObjectSerializerConstructor<In, Out> = {
  new(record: In): ObjectSerializer<In, Out>;
};

abstract class ObjectSerializer<In, Out> extends TypedSerializer<In, Out> {

  toObject(): Out {
    return null as Out;
  }
}

abstract class TypedListSerializer<In, Out> extends TypedSerializer<In, Out> {
  toList(): Out[] {
    return [] as Out[];
  }
}

interface A {
  prop1: string;
}

interface B {
  prop2: string;
}

class RecordSerializer extends ObjectSerializer<A, B> implements B {

  get prop2() {
    return '';
  }
}

interface ListOut {
  arr: B[]
}

class TestListSerializer extends TypedListSerializer<A, ListOut> implements ListOut {
  produceInputs(): A[] {
    return [] as A[];
  }

  get arr() { // <-- fails here, line 63
    return this.serializeArray(RecordSerializer, this.produceInputs());
  }
}

我希望它编译得很好,但它产生了一个错误:

tsc --moduleResolution node --target es6 --lib es6 --noEmit true test.ts 
test.ts(63,7): error TS2416: Property 'arr' in type 'TestListSerializer' is not assignable to the same property in base type 'ListOut'.
  Type '{}[]' is not assignable to type 'B[]'.
    Type '{}' is not assignable to type 'B'.
      Property 'prop2' is missing in type '{}'.

这是link打字稿游乐场

对我来说,TSC 似乎无法正确推断出 RecordSerializer 的实际泛型参数,因此它以 {} 数组而不是 B 数组结束。

添加像 this.serializeArray(...) as B[] 这样的显式转换可以解决问题。 但必须有更好的方法来告诉 Typescript 我想要什么。我错过了什么?

我正在使用 v2.8.1

让打字稿根据一个通用参数推断另一个通用参数通常效果不佳。最简单的解决方案是使用条件类型根据 S

提取输入和输出参数
type GetInFrom<T> = T extends ObjectSerializerConstructor<infer In, any>? In : never; 
type GetOutFrom<T> = T extends ObjectSerializerConstructor<any, infer Out>? Out : never;

serializeObject<S extends ObjectSerializerConstructor<any, any>>(ctor: S, data: GetInFrom<S>): GetOutFrom<S> {
    const serializerInstance = new ctor(data);
    return serializerInstance.toObject();
}

serializeArray<S extends ObjectSerializerConstructor<any, any>>(ctor: S, arr: GetInFrom<S>[]): GetOutFrom<S>[] {
    const fn: ((pIn: GetInFrom<S>) => GetOutFrom<S>) = (data => this.serializeObject(ctor, data));
    return arr.map(fn);
}

playground

上的完整代码