如何在 TypeScript 中进行运行时类型转换? [英] How to do runtime type casting in TypeScript?

查看:27
本文介绍了如何在 TypeScript 中进行运行时类型转换?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前我正在做一个 typescript 项目,我真的很享受 TypeScript 带来的类型推断.但是 - 当从 HTTP 调用获取对象时 - 我可以将它们转换为所需的类型,获取代码完成并调用它们编译时的函数,但是这些会导致错误运行时

Currently I'm working on a typescript project and I'm really enjoying the type inference that TypeScript brings to the table. However - when getting objects from HTTP calls - I can cast them to the desired type, get code completion and call functions on them compile time, but those result in errors runtime

示例:

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'

尽管这编译得很好 - 并且智能感知建议我使用该函数,但它给出了运行时异常.对此的解决方案可能是:

Although this compiles nicely - and intellisense suggests me to use the function, it gives a runtime exception. A solution for this could be:

var somejson = { 'name' : 'Ann' };
var ann = new Person(somejson);

console.log(ann.name);        // 'Ann'
console.log(ann.giveName());  // 'Ann'

但这需要我为所有类型创建构造函数.特别是,在处理树状类型和/或来自 AJAX 调用的集合时,必须遍历所有项目并为每个项目新建一个实例.

But that will require me to create constructors for all my types. In paticular, when dealing with tree-like types and/or with collections coming in from the AJAX-call, one would have to loop through all the items and new-up an instance for each.

所以我的问题是:有没有更优雅的方法来做到这一点?也就是说,转换为类型并立即为其提供原型函数?

So my question: is there a more elegant way to do this? That is, cast to a type and have the prototypical functions available for it immediately?

推荐答案

看看编译后的 JavaScript,你会看到类型断言(casting)消失了,因为它只用于编译.现在你告诉编译器 somejson 对象的类型是 Person.编译器相信你,但在这种情况下,事实并非如此.

Take a look at the compiled JavaScript and you will see the type assertion (casting) disappears because it's only for compiling. Right now you're telling the compiler that the somejson object is of type Person. The compiler believes you, but in this case that's not true.

所以这个问题是运行时 JavaScript 问题.

So this problem is a runtime JavaScript problem.

让这个工作的主要目标是以某种方式告诉 JavaScript 类之间的关系是什么.所以...

  1. 找到一种方法来描述类之间的关系.
  2. 创建一些东西以根据此关系数据自动将 json 映射到类.

有很多方法可以解决这个问题,但我会举出一个例子.这应该有助于描述需要做什么.

There's many ways to solve it, but I'll offer one example off the top of my head. This should help describe what needs to be done.

假设我们有这个类:

class Person {
    name: string;
    child: Person;

    public giveName() {
        return this.name;
    }
}

还有这个json数据:

And this json data:

{ 
    name: 'John', 
    child: {
        name: 'Sarah',
        child: {
            name: 'Jacob'
        }
    }
}

为了自动将其映射为 Person 的实例,我们需要告诉 JavaScript 类型是如何关联的.我们不能使用 TypeScript 类型信息,因为一旦编译,我们就会丢失它.一种方法是在描述此的类型上使用静态属性.例如:

To map this automatically to be instances of Person, we need to tell the JavaScript how the types are related. We can't use the TypeScript type information because we will loose that once it's compiled. One way to do this, is by having a static property on the type that describes this. For example:

class Person {
    static relationships = {
        child: Person
    };

    name: string;
    child: Person;

    public giveName() {
        return this.name;
    }
}

下面是一个可重用函数的示例,该函数根据此关系数据为我们处理创建对象:

Then here's an example of a reusable function that handles creating the objects for us based on this relationship data:

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;
}

现在以下代码将起作用:

Now the following code will work:

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 代码并创建保存类之间关系的对象的东西.这样我们就不需要手动维护关系并担心代码更改.例如,现在使用这种设置重构代码有点风险.我不确定目前是否存在这样的事情,但绝对有可能.

Ideally, the best way would be to use something that actually reads the TypeScript code and creates an object that holds the relationship between classes. That way we don't need to manually maintain the relationships and worry about code changes. For example, right now refactoring code is a bit of a risk with this setup. I'm not sure that something like that exists at the moment, but it's definitely possible.

替代解决方案

我刚刚意识到我已经用稍微不同的解决方案回答了一个类似的问题(虽然不涉及嵌套数据).您可以在此处阅读以获取更多想法:

I just realized I already answered a similar question with a slightly different solution (that doesn't involve nested data though). You can read it here for some more ideas:

JSON 到 TypeScript 类实例?

这篇关于如何在 TypeScript 中进行运行时类型转换?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆