From 579e59fc420d9c686aba317511fe6a52665d631e Mon Sep 17 00:00:00 2001 From: "Dylan R. Johnston" Date: Mon, 13 Jan 2020 09:34:24 +0800 Subject: [PATCH] Allow typescript to typecheck spreading & destructuring promap. This was already achieved by binding promp inside of useMemoizedProfunctorState, but Typescript didn't understand that this was allowed. Changing promp to a class property function allows the same functionality while allowing Typescript to understand that destructuring and spreading the class is safe. --- index.ts | 22 ++++++++-------------- test.js | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 14 deletions(-) diff --git a/index.ts b/index.ts index fc4b89d..1d5913d 100644 --- a/index.ts +++ b/index.ts @@ -6,20 +6,18 @@ export type Lens = {get: Getter; set: Setter}; export type Getter = (outer: T) => S; export type Setter = (newInner: S, prevOuter: T) => T; +interface Promap { + (lens: Lens, args?: any[]): ProfunctorState; + (get: Getter, set: Setter, args?: any[]): ProfunctorState; +} + export class ProfunctorState { constructor(public state: T, public setState: SetState) {} - - promap(lens: Lens, args?: any[]): ProfunctorState; - promap( - get: Getter, - set: Setter, - args?: any[], - ): ProfunctorState; - promap( + promap: Promap = ( a: Getter | Lens, b?: Setter | any[], c?: any[], - ): ProfunctorState { + ): ProfunctorState => { const get = typeof a === 'object' ? a.get : a; const set = typeof a === 'object' ? a.set : (b as Setter); const args = typeof a === 'object' ? (b as any[]) : c; @@ -47,11 +45,7 @@ function useMemoizedProfunctorState( args?: any[], ) { return useMemo( - () => { - const profunctor = new ProfunctorState(state, setState); - profunctor.promap = profunctor.promap.bind(profunctor); - return profunctor; - }, + () => new ProfunctorState(state, setState), args ? args : [state], ); } diff --git a/test.js b/test.js index b656f4c..a7f3634 100644 --- a/test.js +++ b/test.js @@ -208,3 +208,51 @@ test('promap accepts lens object', t => { testRenderer.unmount(); t.end(); }); + +test('promap is spreadable', t => { + t.plan(4); + const f = outer => outer.age; + const g = age => ({ age }); + function Input() { + const level0 = { ...useProfunctorState({ age: 20 }) }; + t.ok(level0.promap, 'promap should be defined after spreading'); + + const level1 = level0.promap({ get: f, set: g }); + return React.createElement('span', null, `My age is ${level1.state}`); + } + + const elem = React.createElement(Input); + const testRenderer = TestRenderer.create(elem); + + const result1 = testRenderer.toJSON(); + t.ok(result1, 'should have rendered'); + t.equal(result1.children.length, 1, 'should have one child'); + t.equal(result1.children[0], 'My age is 20', 'should show 20'); + + testRenderer.unmount(); + t.end(); +}); + +test('promap is destructable', t => { + t.plan(4); + const f = outer => outer.age; + const g = age => ({ age }); + function Input() { + const { promap } = useProfunctorState({ age: 20 }); + t.ok(promap, 'promap should be defined after destructuring'); + + const level1 = promap({ get: f, set: g }); + return React.createElement('span', null, `My age is ${level1.state}`); + } + + const elem = React.createElement(Input); + const testRenderer = TestRenderer.create(elem); + + const result1 = testRenderer.toJSON(); + t.ok(result1, 'should have rendered'); + t.equal(result1.children.length, 1, 'should have one child'); + t.equal(result1.children[0], 'My age is 20', 'should show 20'); + + testRenderer.unmount(); + t.end(); +});