Search code examples
arraysfunctiontypescriptmixedtyped

typescript - return specific class instance from mixed array using typed function


Is it possible to retrieve specific type from mixed array using typed function?

public plugins: (Tool|Service)[] = [];
getTools(): Tool[]{
    return this.plugins.filter(t => t instanceof Tool);
}

So far I have no luck. Typescript is throwing following messageenter image description here

TS2322: Type '(Tool | Service)[]' is not assignable to type 'Tool[]'. Property 'onclick' is missing in type 'Service'.

Is there any way how I can set function type to Tool[] here?

Here is full code:

interface Required {
    id: string
    title: string
    owner: string
    type: 'user' | 'admin'
}

class P {
    id; title; owner; type;
    constructor(config: Required){
        this.id = config.id || 'uniqid';
        this.title = config.title || 'Title';
        this.owner = config.owner || 'user';
        this.type = config.type;
    }
}

interface ToolRequired extends Required{
    onclick: () => void
}

class Tool extends P {
    onclick;
    constructor(config = {} as ToolRequired){
        super(config);
        this.type = 'tool';
        this.onclick = config.onclick
    }
}

class Service extends P {
    constructor(config = {} as Required){
        super(config);
        this.type = 'service'
    }
}

class Storag {
    static types = {
        tool: Tool,
        service: Service,
        undefined: Tool,
    };
    public plugins: (Tool|Service)[] = [];
    setPlugin(config = {} as Required){
        const Type = Storag.types[config.type];
        this.plugins.push( new Type(config) );
    }
    getTools(): Tool[]{
        return this.plugins.filter(t => t instanceof Tool);
    }
}

Solution

  • Just tack on an as Tool[] at the end.

    public plugins: (Tool|Service)[] = [];
    getTools(): Tool[]{
        return this.plugins.filter(t => t instanceof Tool) as Tool[]; // << here
    }
    

    The reason you need to do this is because the Typescript compiler isn't smart enough to know that when you do such a filter it will only return Tools. A .filter on any array will usually return the same type as the previous array, which is what the compiler assumes here - a Tool|Service array.

    The compiler is smart enough to know, however, that a Tool|Service can be reduced down to only Tools - as such, you can do an as Tool[] at the end to tell the compiler I know what I'm doing - the type that ".filter" returns will only be Tools here, and the compiler will listen and respect it as such.

    You can read more about the as keyword here: https://www.typescriptlang.org/docs/handbook/basic-types.html (scroll down or search for "Type assertions").