打字稿,不变记录,泛型和类型集合

时间:2018-05-03 06:03:16

标签: typescript immutable.js

版本:

  • Typescript 2.8
  • 不可变:4.0 RC.9

我试图创建一堆模型类,每个类都有一些共同的属性。现在我想创建一个函数,该函数将使用通用类型输入操作该公共属性,该输入从任何模型获取实例。

简单(和工作)版本:

import { Set, Record } from 'immutable';


class Factory {}
class AnimalFactory extends Factory {
  public static x: Set<{ id: string }> = Set([]);
}
class BeastFactory extends Factory {
  public static x: Set<{ id: string }> = Set([]);
}

function getEntityId<T extends keyof TypeMap>(entity: T) {
  const factory = TypeMap[entity];
  factory.x.add({ id: 'id' })
}

type TypeMap = typeof TypeMap;
const TypeMap = {
  a: AnimalFactory,
  b: BeastFactory,
}

getEntityId中,您可以看到它接受ÀnimalFactoryBeastFactory and attributes and methods inside the attributes all infer and merge the types of both classes, so that i can do factory.x.add({id:&#39;&#39;})`完全输入。但我无法访问仅属于TypeMap的Classes子集的属性。

现在,当我试图将相同的概念应用于不可变记录时,它与上面的例子中的工作方式不同。

这是Record示例:

const animal2Defaults = {
  foo: '',
  x: Set<keyof IAnimalAttributes>([]),
};
export interface IAnimalAttributes {
  foo?: string;
  x?: Set<keyof IAnimalAttributes>;
}

class AnimalFactory2 extends Record<Required<IAnimalAttributes>>(animal2Defaults) {
  public constructor(config: IAnimalAttributes) { super(config); }
}


const beast2Defaults = {
  bar: '',
  x: Set<keyof IBeastAttributes>([]),
};
interface IBeastAttributes {
  bar?: string;
  x?: Set<keyof IBeastAttributes>;
}

class BeastFactory2 extends Record<Required<IBeastAttributes>>(beast2Defaults) {
  public constructor(config: IBeastAttributes) { super(config); }
}

function getEntityId2<T extends keyof TypeMap2>(entity: T, record: TypeMap2[T]) {
  record.x.add('x'); // [ts] Cannot invoke an expression whose type lacks a 
  // call signature. Type '((value: "foo" | "x") => Set<"foo" | "x">) | 
  // ((value: "x" | "bar") => Set<"x" | "bar">)' has no compatible call signatures.

  const unionRecord: TypeMapUnion = record;
  unionRecord.x.add('x'); // [ts] Cannot invoke an expression whose type lacks a call signature. 
  // Type '((value: "foo" | "x") => Set<"foo" | "x">) | ((value: "x" | "bar") => Set<"x" | "bar">)' has no 
  // compatible call signatures.
  // (property) add: ((value: "foo" | "x") => Set<"foo" | "x">) | ((value: "x" | "bar") => Set<"x" | "bar">)

  const injectRecord: AttributeUnionRecords = record; // [ts]
  // Type 'TypeMap2[T]' is not assignable to type 'AttributeUnionRecords'.
  //   Type 'AnimalFactory2 | BeastFactory2' is not assignable to type 'AttributeUnionRecords'.
  //     Type 'AnimalFactory2' is not assignable to type 'AttributeUnionRecords'.
  //       Type 'AnimalFactory2' is not assignable to type 'Record<Required<IAnimalAttributes> | Required<IBeastAttributes>> & Readonly<Required<IBeastAttrib...'.

  if (record instanceof BeastFactory2) {
    record.x.add('x')
  }
}

function createInstance<T extends keyof TypeMap>(entity: T) {
  const instance = new TypeMap2[entity]({}); // [ts] Cannot use 'new' with an expression whose type lacks a call or construct signature.
  // (parameter) entity: T extends "a" | "b"
  instance.x.add('x')
}
type AttributeUnion2 = Required<IBeastAttributes>;
type AttributeUnionRecords = Record<Required<IAnimalAttributes> | Required<IBeastAttributes>>
  & Readonly<Required<IAnimalAttributes> | Required<IBeastAttributes>>;
type TypeMapUnion = AnimalFactory2 | BeastFactory2;
type TypeMap2 = {
  a: AnimalFactory2,
  b: BeastFactory2,
};
const TypeMap2 = {
  a: AnimalFactory2,
  b: BeastFactory2,
}

我遇到的问题是TS正在抛出的错误,请参阅上面的内联评论以了解错误。如果您知道如何解决或如何做到不同,请告诉我。

0 个答案:

没有答案