Typescript:在运行时发现和构造类型



我遇到了一个棘手的Typescript问题,这个问题困扰着我——我如何反映以获得未知的属性类型,以便动态设置属性?

下面是一个我尝试做的简单例子:

class JumpParameters { constructor(data: string) {} }
class RunParameters { constructor(data: string) {} }
// Thing is an object I want to construct dynamically from data
// that I discover at runtime.  *** NOTE: neither makething, nor the
// caller of makething are allowed to know the types in Thing at
// Compile time.  
class Thing {
action?: {
jump?: JumpParameters,
run?: RunParameters,
}
}
function makeThing(name: string, data: string)
{
const output = new Thing();
// *** First problem: How do I get the type of action so that  
// I can look for the properties on the next line?
const actionType = ???; 
for(let property of actionType.Properties) {
if(property.name === name) {
// *** Second Problem: how do I discover the types of the 
// properties on action so that I can construct them?
const subType = ???;
output.action[name] = new subType(data);
}
}
return output;
}

您似乎想表达一个动作可以是Jump或Run。这可以用并集类型表示:

class JumpParameters { constructor(data: string) {} }
class RunParameters { constructor(data: string) {} }
interface JumpAction {
actionType: "Jump";
jump: JumpParameters,
}
interface RunAction {
actionType: "Run",
run: RunParameters
}
type Action = JumpAction | RunAction;
type ActionType = Action["actionType"];
interface Thing {
action?: Action;
}
function makeThing(actionType: ActionType, data: string): Thing
{
let action: Action;
switch (actionType) {
case "Jump":
const jumpAction: JumpAction = {
actionType: "Jump",
jump: new JumpParameters(data)
};
action = jumpAction; 
break;
case "Run":
const runAction: RunAction = {
actionType: "Run",
run: new RunParameters(data)
};
action = runAction; 
break;
}
return {
action
};
}

现在,如果有很多不同的操作类型,您可能需要避免使用switch语句。如果你发现行动通常有相同类型的数据(就像他们目前所做的那样(,我们可以让事情变得更简单:

abstract class ActionParameters<T extends ActionType> {
constructor(data: string) {}
}
class JumpParameters extends ActionParameters<"Jump"> {}
class RunParameters extends ActionParameters<"Run"> {}
const actionParametersCtors: {[k in ActionType]: { new(data: string): ActionParameters<k> }} = {
"Jump": JumpParameters,
"Run": RunParameters, 
}
type ActionType = "Jump" | "Run";
interface Action<T extends ActionType> {
actionType: ActionType,
parameters: ActionParameters<T>,
}
interface Thing<T extends ActionType> {
action?: Action<T>;
}
function makeThing<T extends ActionType>(actionType: T, data: string): Thing<T>
{
const action: Action<T> = {
actionType,
parameters: new actionParametersCtors[actionType](data)
};
return {
action
};
}

注意,在这种情况下,我们仍然需要ActionType和参数构造函数之间的显式映射。

此外,在实现ActionParameters时,它很可能最终不需要那个泛型参数,所以您可以去掉它,使事情变得更简单。

最新更新