I want to derive a union type from a table-like input object. I've succeed with a working solution but I suppose there should be shorter / more straightforward way. Here's the input object:
const _COLUMNS = {
"id": {sortable: true}, // more metadata in real code
"@fullname": {sortable: true},
"@profiles": {sortable: false},
"created_at": {sortable: false},
...
} as const
And here's my solution:
type Column = keyof (typeof _COLUMNS)
// ---
type FindSortable<K extends string, T extends {sortable : boolean}> = T extends {sortable : true} ? K : never
type _Sortable = {
[K in Column] : MakeSortable<K, typeof _COLUMNS[K]>
}
type Sortable = _Sortable[keyof _Sortable] // "id" | "@fullname"
type Order = `${Sortable}:${"asc" | "desc"}` // "id:asc" | "id:desc" | ...
//---
Is there a way to define the above dynamic Sortable
type without a middleman _Sortable
? Here's the repeating structure:
type Sortable =
| FindSortable<"id", typeof _COLUMNS["id"]>
| FindSortable<"@fullname", typeof _COLUMNS["@fullname"]>
...
| FindSortable<K, typeof _COLUMNS[K]> ??
Or in math-like notation:
type Sortable = FindSortable<K, typeof _COLUMNS[K]> ∀ K in Column ??
Yes, you can immediately look up the property values of the mapped type from _Sortable
instead of assigning it to a new type alias:
type Sortable = {
[K in Column]: FindSortable<K, typeof _COLUMNS[K]>
}[Column]
I'm not sure why it matters that you eliminate _Sortable
, but if you don't want to define FindSortable
or even Column
you could do everything in a single type alias:
type Sortable = typeof _COLUMNS extends infer C ?
keyof C extends infer K ?
K extends keyof C ?
C[K] extends { sortable: true } ?
K : never : never : never : never;
// type Sortable = "id" | "@fullname"
but that's just silly 😜.