Search code examples
genericstypescriptgeneric-programming

(TypeScript) How to capture the type provided by the user inside the generic function?


I am new to TypeScript and this is the function I've written:

/**
     * @function getComponent()
     * @description finds and returns the requested type of component, null if not found
     * @return {any} component
     */
    public getComponent<T extends Component>(): T{
        for(let i = 0; i < this.componentList.length; i++){
            if(<T>this.componentList[i] instanceof Component){
                return this.componentList[i] as T;
            }
        }

        return null;
    }

This function is inside the GameObject class which contains a list of Component objects

Component is an abstract function that can be extended by other classes. I want this function to return the type of Component requested by the user. For example something like this:

let gameObject = new GameObject();
let audioComponent = gameObject.getComponent<AudioComponent>(); // no syntax error because AudioComponent extends Component
let myComponent = gameObject.getComponent<foo>(); // syntax error, foo doesn't extend Component

I believe I'm doing something wrong, any help?


Solution

  • You don't know the generic type at runtime, all of the types are being removed by the compiler because it's not supported in javascript.

    If I understand you, then maybe this can do the trick for you:

    abstract class Component {}
    
    class Component1 extends Component {}
    class Component2 extends Component {}
    class Component3 extends Component {}
    
    class ComponentsArray extends Array<Component> {
        public getInstanceFor<T extends Component>(componentClass: { new (): T }) {
            for (var i = 0; i < this.length; i++) {
                if ((<any> this[i].constructor).name === (<any> componentClass).name) {
                    return this[i];
                }
            }
    
            return null;
        }
    }
    
    let array = new ComponentsArray();
    array.push(new Component1());
    array.push(new Component2());
    array.push(new Component3());
    
    let component2: Component2 = array.getInstanceFor(Component2);
    console.log(component2); // Component2 {}
    

    If you'll replace the componentList member you have in your GameObject class from (what I assume is) a regular Array to ComponentsArray then your getComponent function is simple:

    public getComponent<T extends Component>(componentClass: { new (): T }): T {
        return this.componentList.getInstanceFor(componentClass);
    }
    

    And then:

    let gameObject = new GameObject();
    let audioComponent = gameObject.getComponent(AudioComponent);