定义相互依赖的泛型`map:Map <t,(<t =“”>(params:Partial <t>)=> T)>;`

时间:2019-04-25 11:27:57

标签: typescript generics

我需要一个地图,其中地图的key等于函数的返回类型value。我该如何定义呢?像示例中那样的语法是错误的,但是我会尝试解释。

Map<key, value>

  • key是类型T
  • value是接受类型为Partial<T>的参数并返回T
  • 的函数
export class DeserializerService {

  private readonly map: Map<T, (<T>(params: Partial<T>) => T)> = new Map();

  constructor() {
    this.initMap();
  }

  public getDeserializeFunction(type: T): T {
    const func: Function | undefined = this.map.get(type);
    if (!func) {
      throw new Error(`No deserializer found for ${type.name}`);
    }
    return func;
  }

  private initMap(): void {
    this.map.set(Vector2, (params: Partial<Vector2>) => new Vector2(params.x, params.y));
    this.map.set(Vector3, (params: Partial<Vector3>) => new Vector3(params.x, params.y, params.z));
    this.map.set(Euler, (params: Partial<Euler>) => new Euler(params.x, params.y, params.z, params.order));
  }
}

背景

我最初的问题是从通过Web套接字获取的普通对象中获取类实例。

例如,一个转移的three.js Vector3看起来像这样。

{x: -251.89716760817976, y: 0, z: 44.7304924070437}

要将其转换为Vector3实例,我必须运行

const params: Partial<Vector3> = {x: -251.89716760817976, y: 0, z: 44.7304924070437};
const vec3: Vector3 = new Vector3(params.x, params.y, params.z);

因为我有多个具有Three.js和其他库属性的类,所以我想将解析代码外包。我的目标是在自己转移的班级中调用类似的内容。

export class Lantern {
  constructor(params: Partial<Lantern> = {}, deserializerService: DeserializerService) {
    Object.assign(this, params);

    this.position = deserializerService.getDeserializeFunction(Vector3)(this.position);
    this.rotation = deserializerService.getDeserializeFunction(Euler)(this.rotation);

    /* ... */
  }
}

2 个答案:

答案 0 :(得分:1)

您似乎想让<T>类型基于每个地图键。

不幸的是,Map接口不支持此功能(实际上,只有使用某种形式的高阶类型支持,才可能正确处理您要查找的形式-参见例如https://github.com/Microsoft/TypeScript/issues/1213)-尽管即使这样,我认为您的要求仍可能需要创建新的类型/接口。

但是,您可以通过以下想法获得想要的东西(如果命名不适合您的用例,则可以为其命名):

type PartialToFull<K> = (params: Partial<K>) => K;

interface DeserializerMap<K> extends Map<K, PartialToFull<K>> {
  get<T extends K>(key: T): PartialToFull<T> | undefined;
  set<T extends K>(key: T, value: PartialToFull<T>): this;
}

这定义了从T实例到方法(params: Partial<T>) => T的映射。 (编辑:我注意到以后您实际上希望键是T的类/构造函数-因此请参见下文)

您可能只想使用DeserializerMap<any>,或者可能希望所有类型都属于某个其他特定接口K(因此为了完整起见,我将其保留)。

这是通过按方法对getset进行额外的了解后再覆盖Maplib.es2015.collection.d.ts来实现的。

(注意-为了轻松获得这些定义,我从map.forEach复制了K的类型定义,并删除了一些不需要重写的其他方法)

当然-这种方法不是完美的-像T这样的最佳方法可以假设它们是new类型的键,但是并没有绕过:)-但是我相信这可以满足您的要求。

编辑-糟糕-您实际上要从构造函数中获取地图...

哦,等等-我误会了: 看来您希望键为T的“类型”吗?从包含T构造函数的对象的意义上讲,它创建了type PartialToFull<K> = (params: Partial<K>) => K; interface Type<T> extends Function { new (...args: any[]): T; } interface DeserializerMap<K> extends Map<Type<K>, PartialToFull<K>> { get<T extends K>(key: Type<T>): PartialToFull<T> | undefined; set<T extends K>(key: Type<T>, value: PartialToFull<T>): this; } class Vector2 { x: number; y: number; constructor (x: number, y: number) { this.x = x; this.y = y; } }; export class DeserializerService { private readonly map: DeserializerMap<any> = new Map(); constructor() { this.initMap(); } public getDeserializeFunction<T>(type: Type<T>): PartialToFull<T> { const func = this.map.get(type); if (!func) { throw new Error(`No deserializer found for ${type.name}`); } return func; } private initMap(): void { this.map.set(Vector2, params => new Vector2(params.x, params.y)); } } -而不是Type的实例化?

在这种情况下,您需要这样做:

<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %>
<input type="hidden" name="input1" value="<c:out value="${dummyInputValue}"/>"/>

有关{{1}}函数定义的说明,请参见this link

答案 1 :(得分:0)

您需要在服务上定义type参数,并从地图定义中删除<T>,否则它在您发布时起作用:

//                        v-- Here
class DeserializerService<T> {

  map: Map<T, (params: Partial<T>) => T> = new Map();

}

测试:

class Person {
  name: string;
  age: number;
}

const s1 = new DeserializerService<Person>();

s1.map.set(42, null as any); // Error
s1.map.set({ name: 'Bob' }, null as any); // Error
s1.map.set({ name: 'Bob', age: 42 }, null as any); // Works

s1.map.set({ name: 'Bob', age: 42 }, () => ({})); // Error
s1.map.set({ name: 'Bob', age: 42 }, () => ({ name: 'Bob', age: 42 })); // Works