typescript 构造函数泛型参数推断

标签 typescript type-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 typescript Playground

在我看来,TSC 似乎无法正确推断出 RecordSerializer 的实际通用参数,因此它以 {} 数组而不是 数组结尾B.

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

我正在使用 v2.8.1

最佳答案

让 typescript 根据一个通用参数推断另一个通用参数通常效果不佳。最简单的解决方案是使用条件类型根据 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 上的完整代码

关于 typescript 构造函数泛型参数推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50282206/

相关文章:

javascript - 尝试将 Redis 集成到 Node.js (TypeScript) 时出现断言错误

angular - 在查询参数更改时重新解析数据?

c++ - 使用通用私有(private)成员设计模板类

swift - 为什么 Swift nil-coalescing 返回一个 Optional?

angular - 当我运行 ng build --prod 时,我得到 "Error: Cannot read property ' tapAsync' of undefined”

reactjs - 如何创建具有 HTMLAttributes 作为 Prop 的组件

javascript - 带有动态键的 TypeScript array.map()

scala - 在 Scala 中使用类型成员来减少类型冗长是否可行?

compiler-errors - 找到预期的 XYZ ()

types - system-f中有哪些类型和/或术语无法用欣德利·米尔纳(Hindley Milner)表达