Last active
August 16, 2020 15:27
-
-
Save Lucifier129/292a7e2fc81d81319c0350017fcce260 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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