My current type declaration looks like this:
enum Type {
A = 'A',
B = 'B',
C = 'C'
}
type Union =
| {
type: Type.A | Type.B;
key1: string
}
| {
type: Type.C;
key2: string
}
type EnumToUnionMap = {
[T in Type]: {
[k in keyof Extract<Union, {type: T}>]: string
}
}
The complication arises when I try to access EnumToUnionMap[Type.A]
, which results in never
(in reality, it should be a generic key signature like [x: string]: string
, however, due to the Extract<Union, {type: T}>
returning a never
type when T
is either Type.A
or Type.B
). On the other hand, EnumToUnionMap[Type.C]
provides the expected output.
{
type: Type.C,
key2: string
}
The reason behind this behavior is that the type
in EnumToUnionMap[Type.A]
is Type.A | Type.B
, and Type.A != (Type.A | Type.B)
, causing them not to match, resulting in never
.
In essence, what I need to achieve is something like this:
type EnumToUnionMap = {
[T in Type]: {
[k in keyof Extract<Union, T in Union['type']>]: string
}
}
The Reason for This:
I am receiving a response from an alerts endpoint with the following format:
{
type: Type,
key1: string,
key2: string
}
Alerts of both Type.A
and Type.B
provide key1
, while those of Type.C
provide key2
.
I need to map these keys in the response to column names in a grid, where some alert types share common keys but have different display names for the columns:
const columnMap: EnumToUnionMap = {
[Type.A]: {
key1: 'Column name'
// Multiple keys will actually exist in this object for a given `Type`,
// making mapping between `Type -> column name` impossible.
},
[Type.B]: {
key1: 'Different column name'
},
[Type.C]: {
key2: 'Another column name'
}
}
This approach enables me to do something like the following:
const toColumnText = (alert) => columnMap[alert.type]
...
if (alert.type === Type.A) {
const key1ColumnName = toColumnText(alert).key1 // typed as string
const key2ColumnName = toColumnText(alert).key2 // Typescript warns of undefined key
}