I'd like to transform a type to a discriminated union type:
type Current = {
A : number,
B : string,
}
to
type Target= {
type: 'A',
value: number
}
| {
type: 'B',
value: string
}
So that I can discriminate the result.
function handle(result: ToTarget<Current>) {
switch(result.type){
case 'A':
return result.value // number
case 'B':
return result.value // string
}
}
The closest I got was:
type ToTargetA<U> = { code: keyof U, value : U[keyof U] }
// code and value are not in sync so code is 'A | B' and value is 'number | string' but discriminating on code does not narrow value.
type ToTargetB<U, K extends keyof U = any> = { code: keyof K, value : U[K] }
// requires me to specify K as a generic parameter which is not what I want.
I tried several conditional type expressions but couldn't get much closer.
Here's one way to do it:
You can verify that it produces the type you want:
The approach here is to build a mapped type with the same keys
K
as in your original object typeT
, but whose values are the{type: T, value: T[K]}
types you want in the discriminated union. This type ends up becoming{A: {type: "A", value: number}, B: {type: "B". value: string}}
.Then we can look up the union of its property values by indexing into it with
[keyof T]
, producing the desired{type: "A", value: string} | {type: "B", value: number}
discriminated union.The only extra thing I did there was to make it so you could specify what keys to give to the original key names (
KK
, being"type"
in your case) and the original value names (VK
, being"value"
in your case). If you don't want to change that ever, you can hardcode it:Playground link to code