Last active
July 12, 2024 19:59
-
-
Save rybla/200ba647e380df18ff8a6b6914e85c50 to your computer and use it in GitHub Desktop.
in typescript, polymorphic records and variants
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
The idea of this implementation is that a row (`Row`) serves as the | |
specification of either: | |
- a record (`Record<T extends Row>`) which is an object that, for each of the | |
elements of the row, has a key-value mapping from the element's tag to the | |
element's domain | |
- a variant (`Variant<T extends Row>`) which is a union of the row elements. | |
*/ | |
type Tuple = any[] | |
/** | |
* Convert a `Tuple`, | |
* `[..., T_i, ...]`, | |
* into an record, | |
* `{ ..., i: T_i, ... }`. | |
*/ | |
type TupleToRecord<T extends Tuple> = Omit<T, keyof any[]> | |
/* example*/ { | |
type Example = TupleToRecord<[{ tag: 'A', domain: number }, { tag: 'B', domain: string }]> | |
// = { 0: { tag: "A"; domain: number; }; 1: { tag: "B"; domain: string; }; } | |
} | |
type Row = { tag: string, domain: any }[] | |
type Example_Row = [ | |
{ tag: 'A', domain: number }, | |
{ tag: 'B', domain: string }, | |
] | |
/** | |
* Convert a `Row`, | |
* `[..., { tag: Tag_i, domain: Domain_i }, ...]`, | |
* into a record | |
* `{ ..., Tag_i: Domain_i, ... }`. | |
*/ | |
type Record<T extends Row> = { | |
// it's rediculous that this works... | |
// @ts-ignore | |
[Index in keyof TupleToRecord<T> as TupleToRecord<T>[Index]['tag']]: TupleToRecord<T>[Index]['domain'] | |
} | |
/* example*/ { | |
type Example = Record<Example_Row> | |
// = { A: number; B: string; } | |
const e: Example = { | |
'A': 1, | |
'B': "hello world", | |
} | |
} | |
/** | |
* Convert a `Row`, | |
* `[..., { tag: Tag_i, domain: Domain_i }, ...]`, | |
* into a variant, | |
* `... | { tag: Tag_i, domain: Domain_i } | ...`. | |
*/ | |
type Variant<T extends Row> = T[number] | |
/* example*/ { | |
type Example = Variant<Example_Row> | |
// = { tag: "A"; domain: number; } | { tag: "B"; domain: string; } | |
function example(e: Example): string { | |
switch (e.tag) { | |
case 'A': if (e.domain < 0) { return "negative"; } else { return "non-negative"; } | |
case 'B': if (e.domain === "") { return "empty"; } else { return "non-empty"; } | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment