Как добавить динамические свойства в класс TypesScript и поддерживать правильную типизацию - PullRequest
0 голосов
/ 17 мая 2019

У меня есть класс Users, который я экспортирую из файла Users.ts

    export default class Users {}

Затем я экспортирую Users.ts из другого файла, index.ts:

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

У меня есть третий файл, Foo.ts, в котором я хочу динамически создать экземпляр всех экспортируемых классов из index.ts и добавить их в качестве свойств в этот класс:

    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, например:

new Foo(new HttpClient).users

1 Ответ

1 голос
/ 17 мая 2019

Первая часть этого вопроса - создать новый тип, который содержит типы экземпляров импортированного модуля. Для этого мы будем использовать предопределенный условный тип InstanceType для извлечения типа экземпляра класса. Чтобы получить тип модуля, мы будем использовать typeof classes. Оберните все это в отображенный тип, и мы получим:

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 так все получается). Собрав все это вместе, мы получим:

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.
...