Как получить фабрику generic c в конструктор типов разных классов? - PullRequest
1 голос
/ 21 апреля 2020

Учитывая этот пример:

    class OneArgConstructorClass {
        constructor(name: string) {}
    }
    class TwoArgConstructorClass {
        constructor(name: string, age: number) {}
    }
    class Creator<T>
    {
        constructor(tConstructor: new(...args: any[])=> T) {
            const it: T = new tConstructor();
        }
    }

    const e1 = new Creator<OneArgConstructorClass>(OneArgConstructorClass); 
    const e2 = new Creator<OneArgConstructorClass>(TwoArgConstructorClass); 

Почему e1 и e2 являются допустимыми утверждениями? Разве машинопись не должна жаловаться на то, что она не может правильно построить e1 / e2 без правильного количества и типа аргументов? Можно ли сделать этот образец должным образом проверенным типом по отношению к переданным в конструкторы класса?

1 Ответ

1 голос
/ 21 апреля 2020

Причина отсутствия ошибки в том, что ваш параметр tConstructor принимает любое количество параметров любого типа.

Если вы хотите быть более строгими в отношении того, какие параметры разрешены, вы можете использовать типы кортежей, чтобы точно указать, какие параметры принимает tConstructor, например:

class Creator<T>
{
    constructor(tConstructor: new (...args: [string]) => T) {
    //                                      ^^^^^^^^
    // note that args is a tuple containing exactly one item, namely a string
        const it: T = new tConstructor();
        //            ^^^^^^^^^^^^^^^^^^
        // This is now wrong because the constructor takes a string as parameter
    }
}

const e1 = new Creator<OneArgConstructorClass>(OneArgConstructorClass);
const e2 = new Creator<OneArgConstructorClass>(TwoArgConstructorClass);
//                                             ^^^^^^^^^^^^^^^^^^^^^^^
// This is now also wrong because the constructor signatures are not compatible

Если вы сделали ...args Если подпись [string, number], то ваш вызов new tConstructor() все равно будет неправильным, потому что вы не задали ему 2 обязательных параметра, но new Creator<OneArgConstructorClass>(TwoArgConstructorClass) будет в порядке, поскольку теперь он получает правильные 2 параметра.

В отличие от того, что вы можете ожидать, new Creator<OneArgConstructorClass>(OneArgConstructorClass) также больше не выдает ошибку, потому что конструктор, который принимает 1 параметр, будет безопасно игнорировать любые дополнительные параметры, которые он получает.

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ

Если вы хотите типизированный способ вызова конструкторов классов, вам нужно сделать что-то вроде этого:

// Note the constraint that T must be "new"able
class Creator<T extends new (...args: any[]) => any>
{
    // ConstructorParameters is a builtin TS utility that when given a class returns the type of its parameters in a tuple
    // Note that we need to pass the actual parameters in somehow; in this example I've made Creator#constructor take 2 parameters:
    // 1) the class
    // 2) its constructor parameters in a tuple
    constructor(tConstructor: new (...args: ConstructorParameters<T>) => any,
        argsArray: ConstructorParameters<T>) {

        // Construct the class with the arguments array!
        const it: T = new tConstructor(...argsArray);
    }
}

// This will now work
const e1 = new Creator<typeof OneArgConstructorClass>(OneArgConstructorClass, ['hello']);

// This will now *not* work, which is correct, as the constructor signatures don't match
const e2 = new Creator<typeof OneArgConstructorClass>(TwoArgConstructorClass, ['hello', 14]);
//                                                    ^^^^^^^^^^^^^^^^^^^^^^
// Argument of type 'typeof TwoArgConstructorClass' is not assignable to parameter of type 'new (name: string) => any'.

// This *will* work because the signatures match
const e3 = new Creator<typeof TwoArgConstructorClass>(TwoArgConstructorClass, ['hello', 14]); 
...