web-dev-qa-db-fra.com

Génériques TypeScript: 'le type n'est pas attribuable au type T'

J'ai créé une usine qui créera des instances de certaines classes. Je veux utiliser des génériques pour m'assurer que tous les objets retournés proviennent de sous-classes qui étendent des classes abstraites.

Je pensais que la logique de la méthode createInstance montrée ci-dessous pourrait être décrite comme 'createInstance () retournera un type T qui est contraint à être une classe qui étend Animal.

Comme vous pouvez le voir, Lion étend Animal, mais je reçois toujours l'avertissement du compilateur type Lion is not assignable to type T.

abstract class Animal {
    abstract makeSound(): void;
}

class Bear extends Animal {
    public makeSound() {
        console.log('growl');
    }
}

class Lion extends Animal {
    public makeSound() {
        console.log('roar');
    }
}

function createInstance<T extends Animal>(type: string): T {
    switch(type) {
        case 'bear':
            return new Bear(); // 'type Bear is not assignable to type T'
        case 'lion':
            return new Lion(); // 'type Lion is not assignable to type T'
    }
}

createInstance().makeSound();

J'ai lu à la fin des documents TypeScript Generics que:

Lors de la création d'usines en TypeScript à l'aide de génériques, il est nécessaire de faire référence aux types de classe par leurs fonctions constructeurs. Par exemple,

function create<T>(c: {new(): T; }): T {
    return new c();
}

mais je ne veux pas vraiment avoir à passer le constructeur de classe dans la fonction si possible et je voudrais comprendre pourquoi je reçois le not assignable to type T message en premier lieu.

Merci

8
Joe

Si votre fonction retourne toujours un Lion son type de résultat n'est pas vraiment générique. Vous pouvez par exemple écrire create<Tiger>() et votre fonction retournera toujours un Lion. Une véritable fonction générique retournerait une valeur qui respecte le paramètre générique.

Vous pouvez passer le constructeur en argument, comme vous l'avez découvert:

function create<T>(c: {new(): T; }): T {
    return new c();
}

Ou vous pouvez rendre votre fonction non générique et lui faire retourner un Animal ou un Lion. Vous pourriez avoir plus de surcharges, si vous avez une logique basée sur des valeurs d'argument qui déterminent le type de retour:

// Public signatures, we tie function parameter values to return value for specific types
function createInstance(type: "Lion"): Lion 
function createInstance(type: "Tiger"): Tiger 
// Private signature, not visible from outside
function createInstance(type: "Lion" | "Tiger"): Animal {
    if(type === "Lion") {
        return new Lion();
    }
    else if(type === "Tiger") {
        return new Tiger(); 
    }
}
let tiger = createInstance("Tiger"); // will be typed as Tiger
let lion = createInstance("Lion");// will be typed as Lion
let err = createInstance("Lama");// will be an error since the function does not know how to create a Lama