Reputation: 360
I have union of such shape:
type Message =
| { type: 'first', someParam: any } // let it be AMsg
| { type: 'second', someAnotherParam: any, andOneAnother: any }
I want to get an element of this union as a separate type like so:
type AMsg = MessageOfType<'first'> // = { type: 'first', someParam: any }
I've tried to write my own MessageOfType:
type MessageOfType<T extends Message['type']> = Message['type'] extends T ? Message : never
but it allways returns never
.
What should I use to get an element or some elements with type
matching needed value from union?
Upvotes: 5
Views: 1878
Reputation: 360
My final solution is this:
type MessageOfType<T extends Message['type']> = Extract<Message, { type: T }>
It's less magical then extends { type: T }
and uses standard type.
Upvotes: 0
Reputation: 20132
type MessageOfType<
T extends Message['type'],
_M extends Message = Message> = _M extends {type: T} ? _M : never
type First = MessageOfType<'first'>
Explanation:
_M extends Message = Message
local type variable - its generic in order to force TS to narrow the type. Using Message directly would not narrow it_M extends {type: T} ? _M : never
- we say if our _M
has wanted type
property then we want it, if not we take never. As _M
is union then the result will be { type: 'first', someParam: any } | never
. never
is neutral for |
and will be skipped in result we get what we need.Upvotes: 1
Reputation: 5075
Instead of doing type acrobatics you might just want to refactor:
type Message = AMsg | BMsg
type AMsg = { type: 'first', someParam: any }
type BMsg = { type: 'second', someAnotherParam: any, andOneAnother: any }
Upvotes: 2
Reputation: 249536
You can just use the predefined conditional type Extract
for this:
type Message =
| { type: 'first', someParam: any } // let it be AMsg
| { type: 'second', someAnotherParam: any, andOneAnother: any }
type AMsg = Extract<Message, { type: 'first' }>
Upvotes: 7