Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript Generics: 'type is not assignable to type T'

I have made a factory that will create instances of certain classes. I want to use generics to ensure that all of the objects returned are from sub-classes that extend an abstract classes.

I thought that the logic of the createInstance method shown below could be described as 'createInstance() will return a type T that is constrained to be a class that extends Animal.

As you can see, Lion extends Animal, but I still get the compiler warning 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();

I have read at the end of the TypeScript Generics docs that:

When creating factories in TypeScript using generics, it is necessary to refer to class types by their constructor functions. For example,

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

but I don't really want to have to pass in the class constructor into the function if possible and would like to understand why I'm getting the not assignable to type T message in the first place.

Thanks

like image 802
Joe Avatar asked Apr 10 '18 09:04

Joe


People also ask

How do I fix type string is not assignable to type never?

The error "Argument of type is not assignable to parameter of type 'never'" occurs when we declare an empty array without explicitly typing it and attempt to add elements to it. To solve the error, explicitly type the empty array, e.g. const arr: string[] = []; .

Is not assignable to type TypeScript?

The "Type 'string' is not assignable to type" TypeScript error occurs when we try to assign a value of type string to something that expects a different type, e.g. a more specific string literal type or an enum. To solve the error use a const or a type assertion.

What is T type in TypeScript?

'T' is going to be a type declared at run-time instead of compile time. The T variable could be any non-declared variable (I couldn't find a reference, but I would assume any valid set of characters that could be used for a variable names).

How do you pass a generic type in TypeScript?

Assigning Generic ParametersBy passing in the type with the <number> code, you are explicitly letting TypeScript know that you want the generic type parameter T of the identity function to be of type number . This will enforce the number type as the argument and the return value.


1 Answers

If your function always returns a Lion it's result type is not really generic. You could for example write create<Tiger>() and your function would still return a Lion. A true generic function would return a value that honors the generic parameter.

You could pass the constructor as an argument, as you discovered:

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

Or you could make your function not generic, and have it return an Animal or a Lion. You could have more overloads, if you have logic based on argument values that determine the return type:

// 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
like image 165
Titian Cernicova-Dragomir Avatar answered Oct 04 '22 03:10

Titian Cernicova-Dragomir