Как объявить фабричные методы с генериками - PullRequest
1 голос
/ 14 марта 2020
class Context {

}

class MyClass1 {
    constructor(context: Context, argv: number) {
        //do something with context
    }
}

class MyClass2 {
    constructor(context: Context, argv1: string, argv2: number) {
        // do something with context
    }
}

type ExcludeContext<T extends new (context: Context, ...argv: any) => any> =
    T extends new (context: Context, ...argv: infer P) => any ? P : never

type a = ExcludeContext<typeof MyClass1> // [number]
type b = ExcludeContext<typeof MyClass2> // [string, number]

class Factory {
    constructor(private context: Context) { }

    createObj(template: typeof MyClass1, ...argv: ExcludeContext<typeof MyClass1>) {
        return new template(this.context, ...argv)
    }
    createObj2<T extends (typeof MyClass1) | (typeof MyClass2)>(template: T, ...argv: ExcludeContext<T>) {
        // Oops,compiler prompt error for argv 
        // Expected 3 arguments, but got 1 or more.
        // An argument for 'argv' was not provided
        return new template(this.context, ...argv)
    }
}

У меня есть много классов, которые используют contenxt для constructor.
Однако контекст предназначен для передачи из Factory, так что эти классы должны быть созданы Factory.
Я хочу реализовать простое обобщение c метод для создания ob js. Это нормально для createObj, но все пошло не так, когда я попытался объявить обобщенный c метод createObj2.
template был показан как new (context:Context,argv1:never,argv2:number)=>MyClass1|MyClass2

1 Ответ

2 голосов
/ 14 марта 2020

Условные типы, которые все еще имеют неразрешенные параметры типа, обычно не являются чем-то, о чем TS пытается рассуждать. Обычно лучше избегать использования условных типов при реализации обобщенных функций c. Если вы должны использовать их, вероятно, понадобится утверждение типа (return new template(this.context, ...argv as any)).

Однако в этом случае мы можем переписать функцию без каких-либо условных типов, используя сигнатуры конструктора и кортежи в параметрах rest:


class Factory {
  constructor(private context: Context) { }
  createObj<T extends MyClass1 | MyClass2, U extends any[]>(template: new (context: Context, ...args: U) => T, ...argv: U) {
    return new template(this.context, ...argv)
  }
}


let f = new Factory(new Context());
f.createObj(MyClass2, "A", 1)
f.createObj(MyClass1, 1)
f.createObj(MyClass1, "A") // err

Playground Link

...