目前我正在开发一个打字稿项目,我真的很喜欢TypeScript带来的类型推断。但是 - 当从HTTP调用获取对象时 - 我可以将它们转换为所需类型,获取代码完成并在它们上调用函数编译时间,但这些会导致错误运行时 < / p>
示例:
class Person{
name: string;
public giveName() {
return this.name;
}
constructor(json: any) {
this.name = json.name;
}
}
var somejson = { 'name' : 'John' }; // Typically from AJAX call
var john = <Person>(somejson); // The cast
console.log(john.name); // 'John'
console.log(john.giveName()); // 'undefined is not a function'
虽然这很好地编译 - 并且intellisense建议我使用该函数,但它会给出运行时异常。对此的解决方案可能是:
var somejson = { 'name' : 'Ann' };
var ann = new Person(somejson);
console.log(ann.name); // 'Ann'
console.log(ann.giveName()); // 'Ann'
但这需要我为我的所有类型创建构造函数。特别是,当处理类似树的类型和/或来自AJAX调用的集合时,必须循环遍历所有项目并为每个项目新建一个实例。
所以我的问题:有更优雅的方法吗?也就是说,施放到一个类型并立即可用于它的原型功能?
答案 0 :(得分:7)
类的原型可以动态地影响对象:
function cast<T>(obj: any, cl: { new(...args): T }): T {
obj.__proto__ = cl.prototype;
return obj;
}
var john = cast(/* somejson */, Person);
答案 1 :(得分:6)
看看编译好的JavaScript,你会看到断言(强制转换)类型消失,因为它只用于编译。现在,您告诉编译器somejson
对象的类型为Person
。编译器相信你,但在这种情况下,这不是真的。
所以这个问题是运行时JavaScript问题。
让这个工作的主要目标是以某种方式告诉JavaScript类之间的关系是什么。所以......
有很多方法可以解决这个问题,但我会提供一个例子。这应该有助于描述需要做什么。
假设我们有这门课程:
class Person {
name: string;
child: Person;
public giveName() {
return this.name;
}
}
这个json数据:
{
name: 'John',
child: {
name: 'Sarah',
child: {
name: 'Jacob'
}
}
}
要自动将其映射为Person
的实例,我们需要告诉JavaScript类型是如何相关的。我们无法使用TypeScript类型信息,因为我们将在编译后将其丢失。一种方法是在描述此类型的类型上使用静态属性。例如:
class Person {
static relationships = {
child: Person
};
name: string;
child: Person;
public giveName() {
return this.name;
}
}
然后,这是一个可重用函数的示例,它根据此关系数据处理为我们创建对象:
function createInstanceFromJson<T>(objType: { new(): T; }, json: any) {
const newObj = new objType();
const relationships = objType["relationships"] || {};
for (const prop in json) {
if (json.hasOwnProperty(prop)) {
if (newObj[prop] == null) {
if (relationships[prop] == null) {
newObj[prop] = json[prop];
}
else {
newObj[prop] = createInstanceFromJson(relationships[prop], json[prop]);
}
}
else {
console.warn(`Property ${prop} not set because it already existed on the object.`);
}
}
}
return newObj;
}
现在,以下代码将起作用:
const someJson = {
name: 'John',
child: {
name: 'Sarah',
child: {
name: 'Jacob'
}
}
};
const person = createInstanceFromJson(Person, someJson);
console.log(person.giveName()); // John
console.log(person.child.giveName()); // Sarah
console.log(person.child.child.giveName()); // Jacob
理想情况,最好的方法是使用实际读取TypeScript代码的内容并创建一个保存类之间关系的对象。这样我们就不需要手动维护关系并担心代码更改。例如,现在重构代码对于此设置来说有点风险。我不确定此刻是否存在类似的内容,但这绝对是可能的。
替代解决方案
我刚刚意识到我已经用稍微不同的解决方案回答了类似的问题(虽然这并不涉及嵌套数据)。你可以在这里阅读更多的想法:
答案 2 :(得分:1)
您可以使用Object.assign,例如:
var somejson = { 'name' : 'Ann' };
var ann = Object.assign(new Person, somejson);
console.log(ann.name); // 'Ann'
console.log(ann.giveName()); // 'Ann'
但是,如果您有嵌套类,则必须映射抛出对象并为每个元素分配。