es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Typescript – tipos de argumentos del constructor dependiendo del tipo de propiedad de la clase

Tengo un patrón de herencia de clases donde me gustaría que el tipo del constructor de un argumento dependa del tipo de la propiedad de una clase.

Aquí está mi patrón:

class Animal{
    metadata: any;
    constructor(metadata){
        this.metadata = metadata;
    }

    setMetadata(metadata: this['metadata']){
        this.metadata = metadata;
    }
}

class Dog extends Animal{
    metadata: {
        name: string,
        owner: string
    };

    constructor(metadata){
        super(metadata);
    }
}

class Lion extends Animal{
    metadata: {
        free: boolean,
        country: string
    };

    constructor(metadata){
        super(metadata);
    }
}

Imaginemos que creamos un León:

const lion = new Lion({});

La firma de lion.setMetadata es la que espero:

(metadata: {
    free: boolean;
    country: string;
})=> void

y el siguiente código es un error de typescript:

lion.setMetadata({inconsistentKey : 'refused By typescript'})

Ahora me gustaría lograr el mismo comportamiento directamente con el constructor.
El siguiente código debería ser aceptado:

const lion = new Lion({free: true, country: 'somewhere'})

Pero no esto:

const lion = new Lion({})

Esto supone que el constructor de Lion infiere el tipo de sus argumentos a partir del tipo de la instancia de Lion.
Más precisamente, esperaría que lion.constructor tenga la siguiente firma:

(metadata: {
    free: boolean;
    country: string;
})=> Lion

¿Hay alguna manera de lograr esto en typescript?
Porque la palabra clave this en el tipo no está disponible para miembros estáticos de clase, por lo tanto no está disponible para el constructor.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Desafortunadamente, no hay una forma directa de lograr esto en TypeScript. La razón es que la función constructora es un miembro estático de la clase, y como mencionaste, la palabra clave this en el tipo no está disponible para miembros estáticos.

    Sin embargo, puedes usar una función de fábrica para lograr este comportamiento. Aquí tienes un ejemplo utilizando tu código:

    class Animal {
      metadata: any;
    
      constructor(metadata: any) {
        this.metadata = metadata;
      }
    
      setMetadata(metadata: typeof Animal.prototype['metadata']): void {
        this.metadata = metadata;
      }
    }
    
    class Dog extends Animal {
      metadata = {
        name: '',
        owner: '',
      };
    
      constructor(metadata: typeof Dog.prototype['metadata']) {
        super(metadata);
      }
    }
    
    class Lion extends Animal {
      metadata = {
        free: true,
        country: '',
      };
    
      constructor(metadata: typeof Lion.prototype['metadata']) {
        super(metadata);
      }
    }
    
    function createLion(metadata: typeof Lion.prototype['metadata']): Lion {
      return new Lion(metadata);
    }
    

    Ahora puedes usar createLion para crear una instancia de Lion y que el tipo del argumento se infiera a partir de la propiedad metadata:

    const lion = createLion({
      free: true,
      country: 'en algún lugar',
    }); // Válido
    
    const lion2 = createLion({}); // Error: Falta la propiedad 'free'
    

    ¡Espero que esto te ayude! Déjame saber si tienes alguna otra pregunta.

Comments are closed.