I have a event map typed out like so:
type Amount = number
export type EventMap = {
'player:modifyCredits': Amount
'player:modifyEnergy': Amount
'player:die': void
}
In a function I have a create a string representation for an occurred event:
export function actionEffectToLabel<K extends keyof EventMap = keyof EventMap>(effect: [K, EventMap[K]]) {
const [key, value] = effect
switch (key) {
case 'player:modifyEnergy':
return value > 0 ? `+ energy` : `- energy`
case 'player:modifyCredits':
return value > 0 ? `+${value} credits` : `${value} credits`
default:
return undefined
}
}
But TypeScript gives an error for the value comparison lines:
TS2365: Operator > cannot be applied to types EventMap[K] and number
I was under the impression that effect: [K, EventMap[K]]
would tie the type of the value to the key once specified in the case branches.
What I want is that once I test key
to be player:modifyEnergy
, the type system understands that value
has to be Amount
and therefore resolves to the type number
. Is that possible?
I have a event map typed out like so:
type Amount = number
export type EventMap = {
'player:modifyCredits': Amount
'player:modifyEnergy': Amount
'player:die': void
}
In a function I have a create a string representation for an occurred event:
export function actionEffectToLabel<K extends keyof EventMap = keyof EventMap>(effect: [K, EventMap[K]]) {
const [key, value] = effect
switch (key) {
case 'player:modifyEnergy':
return value > 0 ? `+ energy` : `- energy`
case 'player:modifyCredits':
return value > 0 ? `+${value} credits` : `${value} credits`
default:
return undefined
}
}
But TypeScript gives an error for the value comparison lines:
TS2365: Operator > cannot be applied to types EventMap[K] and number
I was under the impression that effect: [K, EventMap[K]]
would tie the type of the value to the key once specified in the case branches.
What I want is that once I test key
to be player:modifyEnergy
, the type system understands that value
has to be Amount
and therefore resolves to the type number
. Is that possible?
1 Answer
Reset to default 1If you make the generic parameter as the mapped type of EventMap
, TS would treat effect
as a discriminated union which is narrowed easily. Since the correlation is done inside the mapped type you don't need even a generic parameter and can set it directly on the argument too.
Playground
type Amount = number
export type EventMap = {
'player:modifyCredits': Amount
'player:modifyEnergy': Amount
'player:die': void
}
export function actionEffectToLabel<T extends {[K in keyof EventMap]: [K, EventMap[K]]}[keyof EventMap]>(effect: T) {
const [key, value] = effect;
switch (key) {
case 'player:modifyEnergy':
return value > 0 ? `+ energy` : `- energy`
case 'player:modifyCredits':
return value > 0 ? `+${value} credits` : `${value} credits`
default:
return undefined
}
}
key
/effect[0]
andvalue
/effect[1]
are independent (and they might be something like'player:modifyCredits' | 'player:modifyEnergy' | 'player:die'
andAmount | void
respectively). What you'd need I think is a discriminated union whereeffect: ['player:modifyCredits', Amount] | ['player:modifyEnergy', Amount] | ['player:die' | void]
. There's probably some way of using mapped types to generate that fromEventMap
. – Bergi Commented Mar 11 at 9:46