zig1375
zig1375

Reputation: 294

TypeScript keeping class inside a Map

It's too complicated to explain what I need to get, let me show a code with some comments.
I think it will be much clearer.

abstract class Base {
    abstract getType(): string;

    static test(): string[] {
        return [];
    }
}
class Foo extends Base {    
    foo =  '123';

    getType(): string {
        return "foo";
    }

    static test(): string[] {
        return ["a", "b", "c"]
    }
}
class Bar extends Base {    
    bar =  '321';

    getType(): string {
        return "bar";
    }
    static test(): string[] {
        return ["a", "b", "c"]
    }
}

// The following map is a temporary solution just to show what I want to get
// I supposed the map has to have another type instead of "any"
const m = new Map<string, any>();
m.set("foo", Foo);
m.set("bar", Bar);

console.log(m.get('foo').test()); // << I need to have access to static functions and it works with "any" type


function func(it: Base) {
    const baseType = it.getType();

    if (m.has(baseType)) {
        const cl = m.get(baseType);

        const item = it as cl; // << error: 'cl' refers to a value, but is being used as a type here. Did you mean 'typeof cl'?
        console.log(item);
    }
}

// I know I could do it by the following way, but in this case if I will want to add a few more new classes I will have to update many palces in the code.
// I'd like just to add a new class into the map without updating the entire code.
function func2(it: Foo|Bar) { 
    const baseType = it.getType();
    console.log(baseType, it)
}


const fooInst = new Foo();
func(fooInst);
func2(fooInst);

This code in the playground.

What I want to get:

Upvotes: 0

Views: 68

Answers (1)

Mackan90096
Mackan90096

Reputation: 354

Map or an Object with a list of classes

In TypeScript, a list of classes can be achieved using a union.

In order for a union to work with static members of a class, you should also use the typeof keyword.

Casting to a type based on a property

In order to cast to a type based on a property of the base object, you need to use type guards.

Type guards

A type guard is a check that makes sure that a given argument is of the type. This can either be the typeof keyword, i.e. typeof arg == "string", or a user-defined typeguard;

function isArgString(arg: any): arg is string => {
    return typeof arg == "string";
}

Recap

To recap, the way to make your map work is using a union, and for your casting, use typeguards:

type myUnion = typeof Foo | typeof Bar;


export const isBar = (arg: Base): arg is Bar => {
    return arg.getType() === 'bar';
}


export const isFoo = (arg: Base): arg is Foo => {
    return arg.getType() === 'foo';
}

Playground Link

Upvotes: 2

Related Questions