Supongamos que tengo dos clases, una clase Dog y una clase Animal . En este ejemplo, Dog extends Animal . Ahora supongamos que tengo dos clases adicionales (una llamada Parent y otra llamada Child ). En TypeScript, puedo escribir los argumentos de la función constructora de mi clase secundaria para que sean los argumentos de la función constructora de mi clase principal usando ConstructorParameters así:
class Parent { constructor(a: Animal, b: number, c: number) {} } class Child extends Parent { constructor(...args: ConstructorParameters<typeof Parent>) { // same as parent's arguments: a: Animal, b: number, c: number super(...args); // doesn't complain, as args matches the arguments for Parent ([a: Animal, b: number, c: number]) } } En este ejemplo, new Child() y new Parent() aceptarían los mismos tipos de argumentos. Pero, ¿cómo podría escribir la función constructora Child para aceptar un tipo más estricto para a , como Dog ?
class Parent { constructor(a: Animal, b: number, c: number) {} } class Child extends Parent { // v----- want to access a in `Child`'s constructor constructor(a: Dog, ...args: ConstructorParameters<typeof Parent>) { super(a, ...args); // <-- complains, I want to pass `a: Dog` and ...args (containing [b: number, c: number]) a.bark(); // use specific Dog methods (that don't exist on Animal) } } // Using: // - new Child(new Animal(), 0, 0); should complain as Animal is not of type Dog // - new Child(new Dog(), 0, 0); should work as Dog is of type Dog Lo anterior no funciona, ya que Child ahora espera que se le pase una instancia de Dog seguida de los tres argumentos para el constructor Parent . Estaba pensando en usar Omit<> o Partial<> en ConstructorParameters para eliminar el tipo a: Animal esperado, pero parece que no pude hacer que funcionaran. También estoy buscando una solución que no implique la necesidad de pasar los argumentos individualmente a la llamada super() (es decir, si agrego argumentos en el constructor de Padres, no tendré que agregarlos al Niño) .
probar tipos condicionales
type RestWithoutOne<K> = K extends [infer WithThis, ...infer WithRest] ? WithRest : never; class Child extends Parent { // v----- want to access a in `Child`'s constructor constructor(a: Dog, ...args: RestWithoutOne<ConstructorParameters<typeof Parent>>) { const a = args[0]; super(a, ...args); // <-- complains, I want to pass `a: Dog` and ...args (containing [b: number, c: number]) a.bark(); // use specific Dog methods (that don't exist on Animal) } }Pero, ¿cómo podría escribir la función constructora
Childpara aceptar un tipo más estricto paraa, comoDog?
Eso suena como una mala idea, violando el principio de sustitución de Liskov : uno debería poder usar Child en todas partes donde se pueda usar Parent .
Por lo tanto, recomendaría genéricos:
class Parent<T extends Animal> { constructor(a: T, b: number, c: number) {} } class Child extends Parent<Dog> { constructor(...args: ConstructorParameters<typeof Parent<Dog>>) { super(...args); const a = args[0]; a.bark(); // use specific Dog methods (that don't exist on Animal) } }