From 15bedb323f5acb8c041efbf5e19eaade51683ced Mon Sep 17 00:00:00 2001 From: slikts Date: Sun, 20 Oct 2019 17:30:01 +0300 Subject: [PATCH] fix(symboltuple): types --- README.md | 2 -- src/Tuple.ts | 76 +++++++++++++++++++++++++++++++++--------- src/tuplerone.ts | 18 +++++----- src/types.ts | 21 +++++++----- test/Tuple.test.ts | 2 +- test/tuplerone.test.ts | 2 +- 6 files changed, 85 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 58613694..73443ac4 100644 --- a/README.md +++ b/README.md @@ -212,8 +212,6 @@ import { UnsafeTuple as Tuple } from 'tuplerone'; Tuple(1, 2, 3) === Tuple(1, 2, 3); // → true ``` -If any of the members are objects, `UnsafeTuple` will work the same as a regular `Tuple` and reuse the same cache. - ### Can't be compared with operators like `<` or `>` tuplerone tuples are not supported by the relation comparison operators like `<`, whereas in a language like Python the following (comparing tuples by arity) would evaluate to true: `(1,) < (1, 2)`. diff --git a/src/Tuple.ts b/src/Tuple.ts index 070adbfe..c085645d 100644 --- a/src/Tuple.ts +++ b/src/Tuple.ts @@ -9,14 +9,16 @@ import { Tuple6, Tuple7, Tuple8, - TupleSymbol, - TupleSymbol2, - TupleSymbol3, - TupleSymbol4, - TupleSymbol5, - TupleSymbol6, - TupleSymbol7, - TupleSymbol8, + SymbolTuple, + SymbolTuple0, + SymbolTuple1, + SymbolTuple2, + SymbolTuple3, + SymbolTuple4, + SymbolTuple5, + SymbolTuple6, + SymbolTuple7, + SymbolTuple8, } from './types'; import { assignArraylike, arraylikeToIterable, getDefaultLazy, isObject } from './helpers'; @@ -77,14 +79,47 @@ export default class Tuple extends (Array as any) implements ArrayLike, It } return getDefaultLazy(tupleKey, () => new Tuple(values, localToken), getLeaf(values)); } - - static symbol(...values: any[]): symbol { - return getDefaultLazy(symbolKey, () => makeSymbol(values), getLeaf(values)); + static symbol( + a: A, + b: B, + c: C, + d: D, + e: E, + f: F, + g: G, + h: H, + ): SymbolTuple8; + static symbol( + a: A, + b: B, + c: C, + d: D, + e: E, + f: F, + g: G, + ): SymbolTuple7; + static symbol( + a: A, + b: B, + c: C, + d: D, + e: E, + f: F, + ): SymbolTuple6; + static symbol(a: A, b: B, c: C, d: D, e: E): SymbolTuple5; + static symbol(a: A, b: B, c: C, d: D): SymbolTuple4; + static symbol(a: A, b: B, c: C): SymbolTuple3; + static symbol(a: A, b: B): SymbolTuple2; + static symbol(a: A): SymbolTuple1; + static symbol(): typeof SymbolTuple0; + static symbol(...values: any[]): any { + return getDefaultLazy(symbolKey, () => Symbol(), getLeaf(values)); } + // The exported member is cast as the same type as Tuple.tuple() to avoid duplicating the overloads static unsafe(...values: any[]): any { return getDefaultLazy( - unsafeKey, + tupleKey, () => new UnsafeTuple(values, localToken), getUnsafeLeaf(values), ); @@ -95,16 +130,22 @@ export default class Tuple extends (Array as any) implements ArrayLike, It } } +// Cache keys for each tuple type const tupleKey = Symbol(); const symbolKey = Symbol(); -const unsafeKey = Symbol(); -const typeOf = (x: unknown): string => typeof x; -const makeSymbol = (values: any[]): symbol => Symbol(String(values.map(typeOf))); + const cache = new WeakishMap(); + +// Token used to prevent calling the constructor from other modules const localToken = Symbol(); + const initWeakish = () => new WeakishMap(); let tuple0: Tuple0; +/** + * Tries to use the first object from value list as the root key and throws + * if there's no objects. + */ export const getLeaf = (values: any[]): WeakishMap => { const rootValue = values.find(isObject); if (!rootValue) { @@ -116,9 +157,12 @@ export const getLeaf = (values: any[]): WeakishMap => { return values.reduce((p, c) => getDefaultLazy(c, initWeakish, p), root); }; +// Unsafe tuples aren't garbage collected so it's more efficient to just use a normal map +const unsafeCache = new Map(); +const initUnsafe = () => new Map(); class UnsafeTuple extends Tuple {} export const getUnsafeLeaf = (values: any[]): Map => - values.reduce((p, c) => getDefaultLazy(c, initWeakish, p), cache); + values.reduce((p, c) => getDefaultLazy(c, initUnsafe, p), unsafeCache); export const { tuple, symbol, unsafe } = Tuple; diff --git a/src/tuplerone.ts b/src/tuplerone.ts index 06c97931..954840bc 100644 --- a/src/tuplerone.ts +++ b/src/tuplerone.ts @@ -23,12 +23,14 @@ export { Tuple6, Tuple7, Tuple8, - TupleSymbol, - TupleSymbol2, - TupleSymbol3, - TupleSymbol4, - TupleSymbol5, - TupleSymbol6, - TupleSymbol7, - TupleSymbol8, + SymbolTuple as SymbolTupleType, + SymbolTuple0, + SymbolTuple1, + SymbolTuple2, + SymbolTuple3, + SymbolTuple4, + SymbolTuple5, + SymbolTuple6, + SymbolTuple7, + SymbolTuple8, } from './types'; diff --git a/src/types.ts b/src/types.ts index 71c095ad..554b61fb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -88,14 +88,19 @@ export interface Tuple8 extends Tuple = symbol; -export type TupleSymbol2 = TupleSymbol; -export type TupleSymbol3 = TupleSymbol; -export type TupleSymbol4 = TupleSymbol; -export type TupleSymbol5 = TupleSymbol; -export type TupleSymbol6 = TupleSymbol; -export type TupleSymbol7 = TupleSymbol; -export type TupleSymbol8 = TupleSymbol; +export type SymbolTuple = { + t: T; +} & symbol; +// tslint:disable-next-line: variable-name +export const SymbolTuple0: SymbolTuple<[never]> = Symbol('SymbolTuple0') as any; +export type SymbolTuple1 = SymbolTuple<[A]>; +export type SymbolTuple2 = SymbolTuple<[A, B]>; +export type SymbolTuple3 = SymbolTuple<[A, B, C]>; +export type SymbolTuple4 = SymbolTuple<[A, B, C, D]>; +export type SymbolTuple5 = SymbolTuple<[A, B, C, D, E]>; +export type SymbolTuple6 = SymbolTuple<[A, B, C, D, E, F]>; +export type SymbolTuple7 = SymbolTuple<[A, B, C, D, E, F, G]>; +export type SymbolTuple8 = SymbolTuple<[A, B, C, D, E, F, G, H]>; export interface Indexable { [i: number]: A; diff --git a/test/Tuple.test.ts b/test/Tuple.test.ts index ca3b2f58..c0a518a4 100644 --- a/test/Tuple.test.ts +++ b/test/Tuple.test.ts @@ -1,7 +1,7 @@ import Tuple from '../src/Tuple'; describe('Tuple', () => { - const a = Object('a'); + const a = {}; const { tuple } = Tuple; it('constructor throws', () => { expect(() => new (Tuple as any)([1, {}], null)).toThrow(); diff --git a/test/tuplerone.test.ts b/test/tuplerone.test.ts index 30224944..79d2da03 100644 --- a/test/tuplerone.test.ts +++ b/test/tuplerone.test.ts @@ -50,7 +50,7 @@ describe('tuple', () => { describe('tuple symbol', () => { it('is of type symbol', () => { - expect(typeof SymbolTuple(1, 2, {})).toBe('symbol'); + expect(typeof SymbolTuple(1 as const, 2, {})).toBe('symbol'); }); it('compares', () => {