Skip to content

Instantly share code, notes, and snippets.

@desmondrawls
Created January 3, 2019 00:30
Show Gist options
  • Save desmondrawls/3c4bb92a2eb8d67223b4c5e46ae210dd to your computer and use it in GitHub Desktop.
Save desmondrawls/3c4bb92a2eb8d67223b4c5e46ae210dd to your computer and use it in GitHub Desktop.
type safe paths for use with redux
import { Path, State, initialState } from "./PathFinder";
describe('PathFinder', () => {
it('works', () => {
let actual = new Path<State>(initialState).path("driverInfo").path("gender").action("unicorn");
expect(actual.payload).toEqual({ "driverInfo": { "gender": "unicorn" } });
});
});
export const initialState = {
driverInfo: {
firstName: '',
lastName: '',
gender: ''
},
products: {
low: {
price: 99,
coverage: "not much coverage"
},
middle: {
price: 150,
coverage: "some coverage"
},
high: {
price: 200,
coverage: "lots of coverage"
}
}
}
export type State = Readonly<typeof initialState>
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends Array<infer U>
? Array<DeepPartial<U>>
: T[P] extends ReadonlyArray<infer U>
? ReadonlyArray<DeepPartial<U>>
: DeepPartial<T[P]>
};
export type AssignmentAction = { type: 'ASSIGNMENT', payload: DeepPartial<State> }
export class Path<S> {
state: S;
breadcrumbs: String[];
constructor(state: S, breadcrumbs: String[] = []) {
this.state = state;
this.breadcrumbs = breadcrumbs;
}
path<K extends keyof S>(key: K): Path<S[K]> {
const next = this.state[key]
return new Path<typeof next>(next, this.breadcrumbs.concat([key as string]));
}
value(): S {
return this.state;
}
action(assignment: S): AssignmentAction {
const leaf: any = { [this.breadcrumbs[this.breadcrumbs.length - 1] as string]: assignment };
const keyTree = this.breadcrumbs.reverse().slice(1).reduce((acc, next) => {
return { [next as string]: acc };
}, leaf);
return {
type: 'ASSIGNMENT',
payload: keyTree
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment