Skip to content

Instantly share code, notes, and snippets.

@rybla
Last active July 12, 2024 19:59
Show Gist options
  • Save rybla/200ba647e380df18ff8a6b6914e85c50 to your computer and use it in GitHub Desktop.
Save rybla/200ba647e380df18ff8a6b6914e85c50 to your computer and use it in GitHub Desktop.
in typescript, polymorphic records and variants
/*
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