Skip to content

Instantly share code, notes, and snippets.

@Lucifier129
Last active August 16, 2020 15:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Lucifier129/292a7e2fc81d81319c0350017fcce260 to your computer and use it in GitHub Desktop.
Save Lucifier129/292a7e2fc81d81319c0350017fcce260 to your computer and use it in GitHub Desktop.
type PickKey<T extends Pick<any, any>> = T extends Pick<any, infer K> ? K : never;
type EmptySelector<T extends object = any> = {
select: <K extends keyof T>(key: K) => NoneEmptySelector<T, Pick<T, K>>;
selectAll: () => CompletedSelector<T>;
};
type NoneEmptySelector<T extends object = any, P extends Pick<T, any> = any> = {
selected: P;
select: <K extends Exclude<keyof T, PickKey<P>>>(key: K) => NoneEmptySelector<T, P & Pick<T, K>>;
unselect: <K extends PickKey<P>>(key: K) => NoneEmptySelector<T, Pick<P, Exclude<PickKey<P>, K>>>;
selectAll: () => CompletedSelector<T>;
clear: () => EmptySelector<T>;
};
type CompletedSelector<T extends object = any> = {
selected: T;
unselect: <K extends keyof T>(key: K) => NoneEmptySelector<T, Pick<T, Exclude<keyof T, K>>>;
clear: () => EmptySelector<T>;
};
const deleteKey = <T extends object, K extends keyof T>(
source: T,
targetKey: K
): Pick<T, Exclude<keyof T, K>> => {
return Object.keys(source).reduce((obj, key) => {
if (key === targetKey) return obj;
obj[key] = selected[key];
return obj;
}, {} as Pick<T, Exclude<keyof T, K>>);
};
const appendKey = <T extends object, P extends Partial<T>, K extends keyof T>(
source: T,
selected: P,
key: K
): P & Pick<T, K> => {
return {
...selected,
[key]: source[key],
} as P & Pick<T, K>;
};
const createCompletedSelector = <T extends object>(
source: T
): CompletedSelector<T> => {
return {
selected: { ...source },
unselect: (unselectKey) => {
return createNoneEmptySelector(source, deleteKey(source, unselectKey));
},
clear: () => createEmptySelector(source),
};
};
const createEmptySelector = <T extends object>(source: T): EmptySelector<T> => {
return {
select: (key) => createNoneEmptySelector(source, {}).select(key),
selectAll: () => createCompletedSelector(source),
};
};
const createNoneEmptySelector = <T extends object, P extends Pick<T, any>>(
source: T,
selected: P
): NoneEmptySelector<T, P> => {
return {
selected: selected,
select: (key) => {
return createNoneEmptySelector(source, appendKey(source, selected, key));
},
unselect: (unselectKey) => {
return createNoneEmptySelector(source, deleteKey(selected, unselectKey));
},
selectAll: () => createCompletedSelector(source),
clear: () => createEmptySelector(source),
};
};
const createSelector = createEmptySelector;
const useSelect = <T extends NoneEmptySelector>(selector: T) => {};
const source = {
a: 1,
b: 2,
c: 3,
d: 4,
};
// uncomment to see type error
// useSelect(createSelector(source))
useSelect(createSelector(source).select('a'));
const selector = createSelector(source)
.select('b')
.select('c')
.select('d')
.select('a')
.unselect('d')
.select('d')
.selectAll()
.unselect('a')
.unselect('b')
.unselect('c')
.unselect('d')
const selected = selector.selected;
// uncomment to see type error
// createSelector({ a: 1, b: 1, c: 1 }).select('d')
// uncomment to see type error
// createSelector({ a: 1, b: 2 }).select('a').select('a');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment