Right now I am struggling with pleasing TypeScript compiler when using generics. I am trying to implement a strategy pattern selector using a JavaScript object. Every strategy shares the same API with several methods, where the only difference is the argument provided for some of the methods of the API, that are options diverse between the different strategies.

This is the code I implemented:

enum Strategy {
    One,
    Two,
    ...
}

export interface StrategiesApi<TOptions> {
    delete(): void;
    execute(options: TOptions): void;
}

export type Strategies<TOptions> = {
    [key in Strategy]: StrategiesApi<TOptions>;
};


const strategies = {
    [Strategy.One]: {
        delete: strategyOneDelete,
        execute: strategyOneExecute,
        ...
    },
    [Strategy.Two]: {
        delete: strategyTwoDelete,
        execute: strategyTwoExecute,
        ...
    },
};

When I am trying to use a function for selecting the strategy like this:

export const getStrategy = <TOptions>(strategy: Strategy): StrategyApi<TOptions> => {
    const strategy = strategies[strategy];

    if (!strategy) {
        throw new Error('Strategy not found');
    }

    return strategy;
};

TypeScript complains about TOptions not to be assignable to the belonging type of the strategy for the argument options in the execute method.

I can fix it explicitly typing the return to avoid that compilation error as:

    ...
    return strategy as StrategyApi<TOptions>;
};

But I do not think is a good idea as it removes the TypeScript type validation.

What would be the best way of implementing this strategy pattern selection with generic argument options? I'd like to avoid having to be completely explicit either in the return type or change the implementation by building a explicit Strategies interface type such:

export interface Strategies {
    [Strategy.One]: {
        delete(): void;
        execute(options: StrategyOneOptions): void;
    },
    [Strategy.Two]: {
        delete(): void;
        execute(options: StrategyTwoOptions): void;
    },
    ...
};
0

There are 0 best solutions below