Используйте классы как переменную и как тип в машинописном тексте - PullRequest
1 голос
/ 20 июня 2020

Мне было интересно, возможно ли что-то подобное.

Я использую Nest JS и TypeORM для своего проекта и хочу использовать транзакции. Я пытаюсь заменить это использование:

getConnection().transaction(async (transactionManager) => {
    const repositoryOne = transactionManager.getCustomRepository(RepostiroyOne) // obviusly types are inherited here
    const repositoryTwo = transactionManager.getCustomRepository(RepostiroyTwo)

   // code in transaction
})

на что-то вроде этого:

const transaction = async(callback, repositories) => {
    const getTransactionRepositories = (manager: EntityManager) => {
        return repositories.map((repository) => manager.getCustomRepository(repository));
    };
    return getManager().transaction(async (transactionManager) =>
        callback(...getTransactionRepositories(transactionManager)),
    );
};

затем в коде для его использования:

transaction(async (repoOne, repoTwo) => {
   // code to run inside transaction
   // but types here are not inherited
   // if i say repoOne: RepositoryOne it is working fine, but I tried to minimize repetition and
   // would like if repoOne is a type of RepositoryOne without manually adding it
}, [RepositoryOne, RepositoryTwo])

Итак, как Я упомянул в первом примере, что я пытаюсь исключить:

const repositoryOne = transactionManager.getCustomRepository(RepositoryOne)

, тогда этот repostiryOne становится:

repositoryOne: RepositoryOne

Итак, в моя функция, 1-й параметр, например repoOne, будет иметь тип RepositoryOne, предоставленный в качестве первого элемента в массиве и т. д.

примечание : строго говоря о декораторах транзакций TypeORM , Я не буду использовать их, так как есть некоторые проблемы с модулем тестирования вложений и декораторами типов.

Любая помощь приветствуется

1 Ответ

1 голос
/ 21 июня 2020

Я считаю, что лучший способ написать это - разбить функцию на вложенные функции (каррирование), использовать параметры отдыха и использовать сопоставленные типы :

import { EntityManager, getManager } from "typeorm";

type MapToInstanceTypes<T> = {
    [key in keyof T]: T[key] extends new() => infer U ? U : never
};

function transaction<T extends (new () => any)[]> (
    ...repositories: T
) {
    return async function(
        callback: (...repos: MapToInstanceTypes<T>) => Promise<void>,
    ) {
        const getTransactionRepositories = (manager: EntityManager) => {
            return repositories.map((repository) => manager.getCustomRepository(repository));
        };
        return getManager().transaction(async (transactionManager) =>
            callback(...getTransactionRepositories(transactionManager) as any),
        );
    }
};

Это можно использовать так:

class X {
    public x: number =  5;
}
class Y {
    public y: number =  5;
}

transaction(X, Y)(async (a, b) => {
    a.x;
    b.y;
});

Вложенные функции требуются для принудительного вывода машинописного текста одного аргумента (типы репозитория, T), а затем сделать обратный вызов типизированным. на основе этого (иначе он станет слишком сложным для правильного вывода машинописного текста).

Остальной параметр необходим для предотвращения вырождения массивов в типы объединения. Например, [0, ""] вырождается в (string | number)[], где на самом деле лучший тип: [number, string].

Сопоставленный тип позволяет вам преобразовывать из типа класса в экземпляр класса. Обычно вы можете сделать это с помощью InstanceType<T>, но я столкнулся с некоторыми проблемами из-за того, что аргумент был массивом, а не объектом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...