Home > Software design >  Typescript - How can I return the Type of the child
Typescript - How can I return the Type of the child

Time:10-26

If I pass a Class I'd like to return its type. Right now I cast the type after returning the parent's type.

I'd Like to avoid the casting and preserving all intellisense features, is there a way?

Below is an example of implementations and what I'd like to achieve

class Component {
    get name(): string {
        return this.constructor.name
    }
}

class Direction extends Component {
    x: number
    y: number

    constructor(x: number = 0, y: number = 0) {
        super()
        this.x = x
        this.y = y
    }
}

class Rectangle extends Component {
    x: number
    y: number
    w: number
    h: number

    constructor(x: number, y: number, w: number, h: number) {
        super()
        this.x = x
        this.y = y
        this.w = w
        this.h = h
    }
}
class Entity {
    components: Map<string, Component>

    constructor(...components: Component[]) {
        this.components = new Map()

        components.forEach(component => this.add(component))
    }

    get(component: Function): Component | undefined {
        return this.components.get(component.name)
    }

    add(component: Component) {
        this.components.set(component.name, component)
    }
}

const player = new Entity(
                    new Rectangle(100, 100, 100, 100),
                    new Direction(1, 1)
                )

const shape = player.get(Rectangle) as Rectangle
const direction = player.get(Direction) as Direction

Example of what I'd like to have

get(component: Function): ?? return the child type somehow ?? | undefined {
    return this.components.get(component.name)
}

const shape = player.get(Rectangle) // No casting

CodePudding user response:

Looks like you just need a generic so that the type of the given constructor is retained in the return type:

    get<C extends new (...args: any[]) => any>(component: C): InstanceType<C> | undefined {
        return this.components.get(component.name) as InstanceType<C>;
    }

Since we're passing a class and not an instance, the type of component should be a constructor. Then in the return type, to get the type of an instance, we use the built-in InstanceType.

Playground


@kikon's shorter (and better) version:

    get<T>(component: new (...args: any[]) => T): T | undefined {
        return this.components.get(component.name) as T;
    }

This removes the need for InstanceType as it lets the compiler infer the instance type for us (as T) and the generic constraint.

Playground

  • Related