Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Change type of an object's property based on another property's value in class variable

TL;DR TypeScript type inference fails in class when using union types

I’ve ran into an issue with typescript when dealing with classes and union types.

This is a basic version of the types that i’ve got:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

type CommonData = {
    name: string
}

type Dog = {
    age: number
} & CommonData

type Bat = {
    wingspan: number
} & CommonData

type Animal = {
    type: "dog"
    data: Dog
} | {
    type: "bat"
    data: Bat
}

The Animal type changes its data type based on the value of the type field.

So, when creating objects using the Animal type, typescript is able to determine what type of data is used for each animal based on the type field. Example below:

const bat: Animal = {
    type: "bat",
    data: {
        name: "tim",
        wingspan: 9000
    }
}

// has type inference, 
bat.data.name

However, when implementing the same type in a class, typescript doesn’t recognize what type of data is returned. Here’s an example of what my current code is like:

class AnimalClass {
    animal: Animal

    constructor (animal: Animal) {
        this.animal = animal
    }
}

const dog = new AnimalClass({
    type: "dog",
    data: {
        name: "bob",
        age: 10
    }
})

// no type inference on data, typescript doesn't know that it's a Dog
dog.animal.data

dog.animal.data has a type of Dog | Bat. Is it possible to alter the code to infer that the type of dog.animal.data is Dog

Thanks in advance 🙂

Here’s the full code: Typescript Playground

>Solution :

You can use a generic annotaion on the Animal class as the following, to make the inference complete:

class AnimalClass<T extends Animal> {
    animal: T;

    constructor (animal: T) {
        this.animal = animal
    }
}

Playground

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading