From 178f528bf031efc2d89ae1a198a966e86318cdb2 Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Wed, 17 Mar 2021 12:20:06 +0100 Subject: [PATCH] Rearrange Dictionary type parameters for improved Record type inference --- .../javascript/packages/core/src/decoder.ts | 4 ++-- .../packages/core/src/dictionary.ts | 10 +++++----- .../javascript/packages/core/src/fold.ts | 8 ++++---- .../javascript/packages/core/src/reader.ts | 4 ++-- .../javascript/packages/core/src/record.ts | 19 +++++++++++++------ .../javascript/packages/core/src/strip.ts | 2 +- .../javascript/packages/core/src/values.ts | 2 +- .../packages/core/test/codec.test.ts | 6 +++--- 8 files changed, 31 insertions(+), 24 deletions(-) diff --git a/implementations/javascript/packages/core/src/decoder.ts b/implementations/javascript/packages/core/src/decoder.ts index 7680e34..b2c148b 100644 --- a/implementations/javascript/packages/core/src/decoder.ts +++ b/implementations/javascript/packages/core/src/decoder.ts @@ -122,8 +122,8 @@ export class Decoder implements TypedDecoder { return this.includeAnnotations ? new Annotated(v) : v; } - static dictionaryFromArray(vs: Value[]): Dictionary, T> { - const d = new Dictionary, T>(); + static dictionaryFromArray(vs: Value[]): Dictionary { + const d = new Dictionary(); if (vs.length % 2) throw new DecodeError("Missing dictionary value"); for (let i = 0; i < vs.length; i += 2) { d.set(vs[i], vs[i+1]); diff --git a/implementations/javascript/packages/core/src/dictionary.ts b/implementations/javascript/packages/core/src/dictionary.ts index fc4715c..4796330 100644 --- a/implementations/javascript/packages/core/src/dictionary.ts +++ b/implementations/javascript/packages/core/src/dictionary.ts @@ -75,14 +75,14 @@ export class KeyedDictionary, V, T = DefaultPointer> extends } } -export class Dictionary extends KeyedDictionary, V, T> { - static isDictionary(x: any): x is Dictionary { +export class Dictionary> extends KeyedDictionary, V, T> { + static isDictionary>(x: any): x is Dictionary { return x?.[DictionaryType] === 'Dictionary'; } - static fromJS(x: object): Dictionary, T> { - if (Dictionary.isDictionary, T>(x)) return x as Dictionary, T>; - const d = new Dictionary, T>(); + static fromJS(x: object): Dictionary> { + if (Dictionary.isDictionary>(x)) return x; + const d = new Dictionary>(); Object.entries(x).forEach(([key, value]) => d.set(key, fromJS(value))); return d; } diff --git a/implementations/javascript/packages/core/src/fold.ts b/implementations/javascript/packages/core/src/fold.ts index b4e7d25..147086b 100644 --- a/implementations/javascript/packages/core/src/fold.ts +++ b/implementations/javascript/packages/core/src/fold.ts @@ -19,7 +19,7 @@ export interface FoldMethods { record(r: Record, Tuple>, T>, k: Fold): R; array(a: Array>, k: Fold): R; set(s: Set, k: Fold): R; - dictionary(d: Dictionary, T>, k: Fold): R; + dictionary(d: Dictionary, k: Fold): R; annotated(a: Annotated, k: Fold): R; @@ -57,7 +57,7 @@ export abstract class ValueFold implements FoldMethods> { set(s: Set, k: Fold>): Value { return s.map(k); } - dictionary(d: Dictionary, T>, k: Fold>): Value { + dictionary(d: Dictionary, k: Fold>): Value { return d.mapEntries(([key, value]) => [k(key), k(value)]); } annotated(a: Annotated, k: Fold>): Value { @@ -110,7 +110,7 @@ export function fold(v: Value, o: FoldMethods): R { return o.array(v, walk); } else if (Set.isSet(v)) { return o.set(v, walk); - } else if (Dictionary.isDictionary, T>(v)) { + } else if (Dictionary.isDictionary(v)) { return o.dictionary(v, walk); } else if (Annotated.isAnnotated(v)) { return o.annotated(v, walk); @@ -153,7 +153,7 @@ export function isPointer(v: Value): v is T { }, array(_a: Array>, _k: Fold): boolean { return false; }, set(_s: Set, _k: Fold): boolean { return false; }, - dictionary(_d: Dictionary, T>, _k: Fold): boolean { + dictionary(_d: Dictionary, _k: Fold): boolean { return false; }, diff --git a/implementations/javascript/packages/core/src/reader.ts b/implementations/javascript/packages/core/src/reader.ts index 4cc6467..3b1607f 100644 --- a/implementations/javascript/packages/core/src/reader.ts +++ b/implementations/javascript/packages/core/src/reader.ts @@ -227,8 +227,8 @@ export class Reader { } } - readDictionary(): Dictionary, T> { - return this.seq(new Dictionary, T>(), + readDictionary(): Dictionary { + return this.seq(new Dictionary(), (k, acc) => { this.skipws(); switch (this.peek()) { diff --git a/implementations/javascript/packages/core/src/record.ts b/implementations/javascript/packages/core/src/record.ts index b7d84e3..23e1099 100644 --- a/implementations/javascript/packages/core/src/record.ts +++ b/implementations/javascript/packages/core/src/record.ts @@ -26,13 +26,20 @@ export interface RecordConstructorInfo, T = DefaultPointer> { arity: number; } +export type InferredRecordType> = + L extends symbol ? (FieldsType extends Tuple> + ? (Exclude extends symbol ? Record : Record) + : (FieldsType extends Tuple> + ? Record + : "TYPE_ERROR_cannotInferFieldsType" & [never])) : + L extends Value ? (FieldsType extends Tuple> + ? Record + : "TYPE_ERROR_cannotMatchFieldsTypeToLabelType" & [never]) : + "TYPE_ERROR_cannotInferPointerType" & [never]; + export function Record>( - label: L, fields: FieldsType): - L extends Value - ? (FieldsType extends Tuple> - ? Record - : never) - : never + label: L, + fields: FieldsType): InferredRecordType { (fields as any).label = label; return fields as any; diff --git a/implementations/javascript/packages/core/src/strip.ts b/implementations/javascript/packages/core/src/strip.ts index 73dba52..6812498 100644 --- a/implementations/javascript/packages/core/src/strip.ts +++ b/implementations/javascript/packages/core/src/strip.ts @@ -33,7 +33,7 @@ export function strip( return (v.item as Value[]).map(walk); } else if (Set.isSet(v.item)) { return v.item.map(walk); - } else if (Dictionary.isDictionary, T>(v.item)) { + } else if (Dictionary.isDictionary(v.item)) { return v.item.mapEntries((e) => [walk(e[0]), walk(e[1])]); } else { return v.item; diff --git a/implementations/javascript/packages/core/src/values.ts b/implementations/javascript/packages/core/src/values.ts index 4e38593..f690a58 100644 --- a/implementations/javascript/packages/core/src/values.ts +++ b/implementations/javascript/packages/core/src/values.ts @@ -28,4 +28,4 @@ export type Compound = // Value to any. | Array> | Set - | Dictionary, T>; + | Dictionary; diff --git a/implementations/javascript/packages/core/test/codec.test.ts b/implementations/javascript/packages/core/test/codec.test.ts index 70678bf..8754e67 100644 --- a/implementations/javascript/packages/core/test/codec.test.ts +++ b/implementations/javascript/packages/core/test/codec.test.ts @@ -140,7 +140,7 @@ describe('encoding and decoding pointers', () => { it('should store pointers embedded in map keys correctly', () => { const A1 = ({a: 1}); const A2 = ({a: 1}); - const m = new Dictionary(); + const m = new Dictionary(); m.set([A1], 1); m.set([A2], 2); expect(m.get(A1)).toBeUndefined(); @@ -157,7 +157,7 @@ describe('common test suite', () => { const samples = decodeWithAnnotations(samples_bin, { decodePointer: decodeDefaultPointer }); const TestCases = Record.makeConstructor<{ - cases: Dictionary, DefaultPointer> + cases: Dictionary }>()(Symbol.for('TestCases'), ['cases']); type TestCases = ReturnType; @@ -257,7 +257,7 @@ describe('common test suite', () => { } const tests = (peel(TestCases._.cases(peel(samples) as TestCases)) as - Dictionary, DefaultPointer>); + Dictionary); tests.forEach((t0: Value, tName0: Value) => { const tName = Symbol.keyFor(strip(tName0) as symbol)!; const t = peel(t0) as Record;