Search code examples
typescriptdictionarygenericstypes

Why does TypeScript not allow child class constructors as Map values?


I am trying to construct a Map in TypeScript, which maps some key to a constructor of a child class of a given parent class, using the type of that parent class as a generic argument for the Map constructor:

abstract class Fruit {
    protected juiceAmount: number;
    constructor(juiceAmount: number) {
        this.juiceAmount = juiceAmount;
    }
}

class Apple extends Fruit {
    protected variation: string = "Red delicious";
}

class Watermelon extends Fruit {
    protected numSeeds: number = 1000;
}

const plantFruits: Map<string, typeof Fruit> = new Map([
    [ "Malus domestica", Apple ],
    [ "Citrullus lanatus", Watermelon ]
]);

For some reason, the TypeChecker doesn't consider this valid (please see link to TypeScript Playground for the full error message, as it's very long). Why is that? How can I make this work, without resorting to removing type-safety by doing i.e. Apple as any?


Solution

  • Adding the generic type definition to the Map constructor works:

    abstract class Fruit {
        protected juiceAmount: number;
        constructor(juiceAmount: number) {
            this.juiceAmount = juiceAmount;
        }
    }
    
    class Apple extends Fruit {
        protected variation: string = "Red delicious";
    }
    
    class Watermelon extends Fruit {
        protected numSeeds: number = 1000;
    }
    
    const plantFruits: Map<string, typeof Fruit> = new Map<string, typeof Fruit>([
        [ "Malus domestica", Apple ],
        [ "Citrullus lanatus", Watermelon ]
    ]);
    

    I'm guessing the compiler wasn't able to infer the typing just from the provided arguments, and needed the explicit definition.