如何向 TypesScript 类添加动态属性并保持正确的类型 [英] How to add dynamic properties to TypesScript class and maintain correct typings

查看:13
本文介绍了如何向 TypesScript 类添加动态属性并保持正确的类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个从文件 Users.ts

    export default class Users {}

然后我从另一个文件index.ts导出Users.ts:

I am then exporting Users.ts from another file, index.ts:

    // classes
    export {default as Users} from './Users'

我有第三个文件 Foo.ts,我想在其中动态实例化从 index.ts 导出的所有类,并将它们作为属性添加到该类中:

I have a third file, Foo.ts, where I want to dynamically instantiate all the exported classes from index.ts and add them as properties to that class:

    import * as classes from './index'

    class Foo {
        constructor() {
           const httpClient = new HttpClient()
        }

        _addClasses() {
           for (const class in classes) {
             this[class] = new classes[class](this.httpClient);
           }
        }
    }

我的问题是,如何将正确的类型添加到 Foo,以便我可以在 IDE 中为 .users 获得正确的自动完成功能,例如:

My question is, how can I add the correct types to Foo, so that I can get the correct autocompletion in the IDE for .users like:

new Foo(new HttpClient).users

推荐答案

这个问题的第一部分是创建一个包含导入模块的实例类型的新类型.为此,我们将使用预定义的条件类型 InstanceType 来提取类的实例类型.要获取模块的类型,我们将使用 typeof classes.将它全部包装在一个映射类型中,我们得到:

The first part of this question is to create a new type that contains the instance types of the imported module. To do this, we will use the predefined conditional type InstanceType to extract the instance type of a class. To get the type of the module we will use typeof classes. Wrap it all in a mapped type and we get:

type ClassInstances = {
    [P in keyof typeof classes]: InstanceType<typeof classes[P]>
}

// For the example above this is equivalent to 
type ClassInstances = {
    Users: classes.Users;
}

现在我们需要将这些新属性放入类中.要做到这一点而不显式定义它们,我们可以使用一个空类表达式作为 Foo 的基类,并断言这个空类返回的实例具有这些成员(实际上并没有,但我们以及 _addClasses 中的这些成员,所以一切都解决了).综合起来,我们得到:

Now we need to get these new properties into the class. To do this without defining them explicitly, we can use an empty class expression as the base class for Foo and assert that the instance returned by this empty class has those members (it doesn't really, but we and these members in _addClasses so it all works out). Putting it all together we get:

import * as classes from './index';

type ClassInstances = {
    [P in keyof typeof classes]: InstanceType<typeof classes[P]>
}

class Foo extends (class {} as new () => ClassInstances) {
    httpClient: HttpClient;
    constructor() {
        super();
        this.httpClient = new HttpClient()
        this._addClasses();
    }

    _addClasses() {
        for (const cls of Object.keys(classes) as Array<keyof typeof classes>) {
            this[cls] = new classes[cls](this.httpClient);
        }
    }
}

new Foo().Users // ok now name is the same as the name used in the export in index.ts so it's upper case. No string manipulation on string properties.

这篇关于如何向 TypesScript 类添加动态属性并保持正确的类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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