How to dynamically set the type of constructor function method in TypeScript

You can do that via a generic type parameter and a readonly array of animals, like this;

class MyAnimal<Animal> {
    constructor(public animals: readonly Animal[]) {
    }
    getAnimal(url: Animal) {
        console.log(url);
    }
}

const animalTest = new MyAnimal(['sheep', 'dog', 'cat'] as const);
animalTest.getAnimal('mouse'); // Error as desired
animalTest.getAnimal('sheep'); // Works

Playground link

TypeScript can infer the string literal union type to provide as the type argument for the Animal type parameter because the array is readonly (which we satisfy when calling the constructor via as const), so TypeScript knows it won't change at runtime.


In order to infer literal type, you can use variadic tuple types:

type Animal<Name extends string> = {
    name: Name,
    url: `https://${string}`
}

class MyAnimal<
    Name extends string,
    Item extends Animal<Name>,
    List extends Item[]
    > {
    constructor(public animals: [...List]) {
    }

    // You should get "url" from infered list and not from Item["url"]
    getAnimal<Url extends List[number]['url']>(url: Url) {
        console.log(url);
    }
}

const animalTest = new MyAnimal(
    [
        { name: 'sheep', url: 'https://sheep.com', },
        { name: 'dog', url: 'https://dog.com', },
        { name: 'cat', url: 'https://cat.com', }
    ]);

animalTest.getAnimal('https://dog.com'); // ok
animalTest.getAnimal('https://mouse.com'); // expected error

Playground

If you want to learn more about literal type inference, you can check my article. The goal is to provide as much as possible constraints to generic parameter. If there are enough constraints TS will infer literal type.