Skip to content

Commit

Permalink
Allow typescript to typecheck spreading & destructuring promap.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DylanRJohnston authored and staltz committed Jan 13, 2020
1 parent d4d2862 commit 579e59f
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 14 deletions.
22 changes: 8 additions & 14 deletions index.ts
Expand Up @@ -6,20 +6,18 @@ export type Lens<T, S> = {get: Getter<T, S>; set: Setter<T, S>};
export type Getter<T, S> = (outer: T) => S;
export type Setter<T, S> = (newInner: S, prevOuter: T) => T;

interface Promap<T> {
<S>(lens: Lens<T, S>, args?: any[]): ProfunctorState<S>;
<S>(get: Getter<T, S>, set: Setter<T, S>, args?: any[]): ProfunctorState<S>;
}

export class ProfunctorState<T> {
constructor(public state: T, public setState: SetState<T>) {}

promap<S>(lens: Lens<T, S>, args?: any[]): ProfunctorState<S>;
promap<S>(
get: Getter<T, S>,
set: Setter<T, S>,
args?: any[],
): ProfunctorState<S>;
promap<S>(
promap: Promap<T> = <S>(
a: Getter<T, S> | Lens<T, S>,
b?: Setter<T, S> | any[],
c?: any[],
): ProfunctorState<S> {
): ProfunctorState<S> => {
const get = typeof a === 'object' ? a.get : a;
const set = typeof a === 'object' ? a.set : (b as Setter<T, S>);
const args = typeof a === 'object' ? (b as any[]) : c;
Expand Down Expand Up @@ -47,11 +45,7 @@ function useMemoizedProfunctorState<T>(
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],
);
}
Expand Down
48 changes: 48 additions & 0 deletions test.js
Expand Up @@ -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();
});

0 comments on commit 579e59f

Please sign in to comment.