preserves/implementations/javascript/src/record.ts

72 lines
2.8 KiB
TypeScript
Raw Normal View History

2021-02-22 19:00:15 +00:00
import { DefaultPointer, is, Value } from "./values";
2021-02-25 18:37:22 +00:00
export type Tuple<T> = Array<T> | [T];
2021-02-25 18:37:22 +00:00
export type Record<LabelType extends Value<T>, T extends object = DefaultPointer>
= Array<Value<T>> & { label: LabelType };
2021-02-25 18:37:22 +00:00
export type RecordGetters<L extends Value<T>, T extends object, Fs> = {
[K in string & keyof Fs]: (r: Record<L, T>) => Fs[K];
};
2021-02-25 18:37:22 +00:00
export type CtorTypes<Fs, Names extends Tuple<keyof Fs>, T extends object> =
{ [K in keyof Names]: Fs[keyof Fs & Names[K]] } & any[];
2021-02-25 18:37:22 +00:00
export interface RecordConstructor<L extends Value<T>, Fs, Names extends Tuple<keyof Fs>, T extends object = DefaultPointer> {
(...fields: CtorTypes<Fs, Names, T>): Record<L, T>;
constructorInfo: RecordConstructorInfo<L, T>;
isClassOf(v: any): v is Record<L, T>;
_: RecordGetters<L, T, Fs>;
};
2021-02-25 18:37:22 +00:00
export interface RecordConstructorInfo<L extends Value<T>, T extends object = DefaultPointer> {
label: L;
arity: number;
}
2021-02-25 18:37:22 +00:00
export function Record<L extends Value<T>, T extends object = DefaultPointer>(
label: L, fields: Array<Value<T>>): Record<L, T>
{
(fields as any).label = label;
return fields as Record<L, T>;
}
2021-02-25 18:37:22 +00:00
export namespace Record {
export function isRecord<L extends Value<T>, T extends object = DefaultPointer>(x: any): x is Record<L, T> {
return Array.isArray(x) && 'label' in x;
}
2021-02-25 18:37:22 +00:00
export function fallbackToString (_f: Value<any>): string {
return '<unprintable_preserves_field_value>';
}
2021-02-25 18:37:22 +00:00
export function constructorInfo<L extends Value<T>, T extends object = DefaultPointer>(
r: Record<L, T>): RecordConstructorInfo<L, T>
{
return { label: r.label, arity: r.length };
}
2021-02-25 18:37:22 +00:00
export function isClassOf<L extends Value<T>, T extends object = DefaultPointer>(
ci: RecordConstructorInfo<L, T>, v: any): v is Record<L, T>
{
return (Record.isRecord(v)) && is(ci.label, v.label) && (ci.arity === v.length);
}
2021-02-25 18:37:22 +00:00
export function makeConstructor<Fs, T extends object = DefaultPointer>()
: (<L extends Value<T>, Names extends Tuple<keyof Fs>>(label: L, fieldNames: Names) =>
RecordConstructor<L, Fs, Names, T>)
{
return <L extends Value<T>, Names extends Tuple<keyof Fs>>(label: L, fieldNames: Names) => {
const ctor: RecordConstructor<L, Fs, Names, T> =
((...fields: CtorTypes<Fs, Names, T>) =>
Record(label, fields)) as RecordConstructor<L, Fs, Names, T>;
const constructorInfo = { label, arity: fieldNames.length };
ctor.constructorInfo = constructorInfo;
ctor.isClassOf = (v: any): v is Record<L, T> => Record.isClassOf(constructorInfo, v);
(ctor as any)._ = {};
fieldNames.forEach((name, i) => (ctor._ as any)[name] = (r: Record<L, T>) => r[i]);
return ctor;
};
}
}