I've got a project in Typescript 2.0 and I'm using es6
as my module, with the lib es2017
included so I can use maps. I've got everything working except I seem to need to do some unnecessary casts.
static readonly gameStateMap: Map<GameState, Phaser.State> = new Map([
[GameState.PONG, new PongState() as Phaser.State],
[GameState.BREAKOUT, new BreakoutState() as Phaser.State]
]);
Why do I need to cast them to Phaser.State
? Since they both directly extend Phaser.State
I thought I should be able to just stick them in the map with no problem.
I thought maybe I just needed to refine my generics declaration, so I tried:
Map<GameState, V extends Phaser.State>
.
I see that syntax used in Typescript's generics docs.
But it doesn't work. Is this an ES6 generic and that's my problem? How can I fix this to not need to cast?
Also, this issue only occurs if the classes in question have different properties. The error is:
error TS2345: Argument of type '((GameState | PongState)[] | (GameState | BreakoutState)[])[]' is not assignable to parameter of type 'Iterable<[GameState, State]>'.
TypeScript fully supports generics as a way to introduce type-safety into components that accept arguments and return values whose type will be indeterminate until they are consumed later in your code.
Generics allow creating 'type variables' which can be used to create classes, functions & type aliases that don't need to explicitly define the types that they use. Generics makes it easier to write reusable code.
The Map is a new data structure introduced in ES6 so it is available to JavaScript as well as TypeScript. A Map allows storing key-value pairs (i.e. entries), similar to the maps in other programming languages e.g. Java HashMap.
There is something wrong if your system. You need to provide more info.
It is working in the playground
enum GameState {
PONG,
BREAKOUT
}
namespace Phaser {
export interface State { }
}
class PongState implements Phaser.State { }
class BreakoutState implements Phaser.State { }
let gameStateMap: Map<GameState, Phaser.State> = new Map([
[GameState.PONG, new PongState()],
[GameState.BREAKOUT, new BreakoutState()]
]);
UPDATE:
The issue you face is that you declare type manually and rely on inferring. The inferring can only do so much. In this case, the two inner array can't be inferred to a common type, thus they ended up as [{}, {}]
.
The solution is to use the generic directly:
enum GameState {
PONG,
BREAKOUT
}
namespace Phaser {
export interface State { }
}
class PongState implements Phaser.State { a: string }
class BreakoutState implements Phaser.State { b: string }
let gameStateMap = new Map<GameState, Phaser.State>([
[GameState.PONG, new PongState()],
[GameState.BREAKOUT, new BreakoutState()]
]);
When you write it in this way
let gameStateMap: Map<GameState, Phaser.State> = new Map(...)
compiler first tries to infer a type for new Map
, then checks if it's compatible with the type declared for gameStateMap
.
In your case, when arrays with elements having different types are involved, compiler fails to infer common type for them and uses union type instead (the intended type for Map
argument apparently is [GameState, Phaser.State][]
).
Compiler needs a little help with this, you can provide it by giving explicit generic arguments to Map
constructor, and then you can omit explicit type declaration for gameStateMap
:
enum GameState { PONG, BREAKOUT };
namespace Phaser {
export interface State { }
}
class PongState implements Phaser.State { a: string }
class BreakoutState implements Phaser.State { b: string}
let gameStateMap = new Map< GameState, Phaser.State > ([
[GameState.PONG, new PongState()],
[GameState.BREAKOUT, new BreakoutState()]
]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With