我需要一个地图,其中地图的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);
/* ... */
}
}
答案 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
(因此为了完整起见,我将其保留)。
这是通过按方法对get
和set
进行额外的了解后再覆盖Map
和lib.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