I'm trying to create a map of functions for a game I'm making. Basically, I have an Entity
base interface and a bunch of others entities (e.g. Monster
) extending from it, and I need a map of functions that can have any string as a key and, as a value, a function which accepts a subtype extending Entity
as an argument, and has specific code for that particular subentity.
The problem is I need to call some of those functions by its key, but I need to also pass a subtype as argument. Of course, Typescript doesn't allow this due to function params being contravariants. I think I have a decent understanding of the why (but further explanations are always welcomed), what I want to know if there's any way to overcome this limitation.
The following is not my actual code, but an example of what I'm attempting to do:
interface Shape { type: string}
interface Triangle extends Shape { type: 'TRIANGLE'}
interface Square extends Shape { type: 'SQUARE' }
interface Circle extends Shape { type: 'CIRCLE' }
const triangle: Triangle = { type: 'TRIANGLE' }
type Shapes = Triangle | Square | Circle
type FunctionMap = {
[k: string]: (shape: Shapes) => void
}
const map: FunctionMap = {
'some-key': (shape: Triangle) => {} // Typescript error here
}
type Key = keyof typeof map
const myFunc = <T extends Shapes>(shape: T, key: Key) => {
map[key](shape)
}
myFunc(triangle, 'some-key)
And this is the error I'm getting:
Type '(shape: Triangle) => void' is not assignable to type '(shape: Shapes) => void'.
Types of parameters 'shape' and 'shape' are incompatible.
Type 'Shapes' is not assignable to type 'Triangle'.
Type 'Square' is not assignable to type 'Triangle'.
Types of property 'type' are incompatible.
Type '"SQUARE"' is not assignable to type '"TRIANGLE"'.
Here's a playground link, just in case.
Of course, I could make the functions to accept an Entity
(or Shape
in the example) as argument, and use typeguards, but this feels like cheating, pollutes the code, and goes against the whole idea of types. I also have checked this article and several others, without success.
Thanks in advance!
(shape: S, key: K) => myFunc(shape, key)`.– phoc Oct 31 '22 at 13:14