diff --git a/types/recoil/index.d.ts b/types/recoil/index.d.ts index b38ceeca1a..08ee00d641 100644 --- a/types/recoil/index.d.ts +++ b/types/recoil/index.d.ts @@ -177,3 +177,92 @@ export class RecoilValueReadOnly extends AbstractRecoilValueReadonly {} export type RecoilValue = RecoilValueReadOnly | RecoilState; export function isRecoilValue(val: unknown): val is RecoilValue; + +/** Utilities */ + +// bigint not supported yet +export type Primitive = undefined | null | boolean | number | symbol | string; + +export type SerializableParam = Primitive | SerializableParam[] | { [key: string]: SerializableParam }; + +export interface AtomFamilyOptions { + key: NodeKey; + dangerouslyAllowMutability?: boolean; + default: RecoilValue | Promise | T | ((param: P) => T | RecoilValue | Promise); +} + +export function atomFamily( + options: AtomFamilyOptions, +): (param: P) => RecoilState; + +export interface ReadOnlySelectorFamilyOptions { + key: string; + get: (param: P) => (opts: { get: GetRecoilValue }) => Promise | RecoilValue | T; + // cacheImplementation_UNSTABLE?: () => CacheImplementation>, + // cacheImplementationForParams_UNSTABLE?: () => CacheImplementation< + // RecoilValue, + // >, + dangerouslyAllowMutability?: boolean; +} + +export interface ReadWriteSelectorFamilyOptions { + key: string; + get: (param: P) => (opts: { get: GetRecoilValue }) => Promise | RecoilValue | T; + set: ( + param: P, + ) => ( + opts: { set: SetRecoilState; get: GetRecoilValue; reset: ResetRecoilState }, + newValue: T | DefaultValue, + ) => void; + // cacheImplementation_UNSTABLE?: () => CacheImplementation>, + // cacheImplementationForParams_UNSTABLE?: () => CacheImplementation< + // RecoilValue, + // >, + dangerouslyAllowMutability?: boolean; +} + +export function selectorFamily( + options: ReadWriteSelectorFamilyOptions, +): (param: P) => RecoilState; + +export function selectorFamily( + options: ReadOnlySelectorFamilyOptions, +): (param: P) => RecoilValueReadOnly; + +export function constSelector(constant: T): RecoilValueReadOnly; + +export function errorSelector(message: string): RecoilValueReadOnly; + +export function readOnlySelector(atom: RecoilValue): RecoilValueReadOnly; + +export function noWait(state: RecoilValue): RecoilValueReadOnly>; + +export type UnwrapRecoilValue = T extends RecoilValue ? R : never; + +export type UnwrapRecoilValueLoadables> | { [key: string]: RecoilValue }> = { + [P in keyof T]: Loadable>; +}; + +export function waitForNone> | [RecoilValue]>( + param: RecoilValues, +): RecoilValueReadOnly>; + +export function waitForNone }>( + param: RecoilValues, +): RecoilValueReadOnly>; + +export function waitForAny> | [RecoilValue]>( + param: RecoilValues, +): RecoilValueReadOnly>; + +export function waitForAny }>( + param: RecoilValues, +): RecoilValueReadOnly>; + +export function waitForAll> | [RecoilValue]>( + param: RecoilValues, +): RecoilValueReadOnly>; + +export function waitForAll }>( + param: RecoilValues, +): RecoilValueReadOnly>; diff --git a/types/recoil/recoil-tests.ts b/types/recoil/recoil-tests.ts index d77b59e359..a13d7c1297 100644 --- a/types/recoil/recoil-tests.ts +++ b/types/recoil/recoil-tests.ts @@ -13,6 +13,15 @@ import { useRecoilCallback, isRecoilValue, RecoilState, + atomFamily, + selectorFamily, + constSelector, + errorSelector, + readOnlySelector, + noWait, + waitForNone, + waitForAny, + waitForAll, } from 'recoil'; // import { atomFamily } from 'recoil/utils'; @@ -31,13 +40,18 @@ const mySelector1 = selector({ get: () => 5, }); +const mySelector2 = selector({ + key: 'asds', + get: () => '', +}); + // $ExpectError selector({ key: 'asdfasfds', get: () => '', }) as RecoilValueReadOnly; -const readOnlySelector = selector({ +const readOnlySelectorSel = selector({ key: 'asdfasf', get: ({ get }) => { get(myAtom) + 10; @@ -56,8 +70,8 @@ const writeableSelector = selector({ set(myAtom, 5); reset(myAtom); - set(readOnlySelector, 2); // $ExpectError - reset(readOnlySelector); // $ExpectError + set(readOnlySelectorSel, 2); // $ExpectError + reset(readOnlySelectorSel); // $ExpectError }, }); @@ -68,7 +82,7 @@ RecoilRoot({ set(myAtom, 5); setUnvalidatedAtomValues(new Map()); - set(readOnlySelector, 2); // $ExpectError + set(readOnlySelectorSel, 2); // $ExpectError setUnvalidatedAtomValues({}); // $ExpectError }, }); @@ -92,33 +106,33 @@ useRecoilValue(nsAtom); // $ExpectError useRecoilValue(myAtom); useRecoilValue(mySelector1); -useRecoilValue(readOnlySelector); +useRecoilValue(readOnlySelectorSel); useRecoilValue(writeableSelector); useRecoilValue({}); // $ExpectError useRecoilValueLoadable(myAtom); -useRecoilValueLoadable(readOnlySelector); +useRecoilValueLoadable(readOnlySelectorSel); useRecoilValueLoadable(writeableSelector); useRecoilValueLoadable({}); // $ExpectError useRecoilState(myAtom); useRecoilState(writeableSelector); -useRecoilState(readOnlySelector); // $ExpectError +useRecoilState(readOnlySelectorSel); // $ExpectError useRecoilState({}); // $ExpectError useRecoilStateLoadable(myAtom); useRecoilStateLoadable(writeableSelector); -useRecoilStateLoadable(readOnlySelector); // $ExpectError +useRecoilStateLoadable(readOnlySelectorSel); // $ExpectError useRecoilStateLoadable({}); // $ExpectError useSetRecoilState(myAtom); useSetRecoilState(writeableSelector); -useSetRecoilState(readOnlySelector); // $ExpectError +useSetRecoilState(readOnlySelectorSel); // $ExpectError useSetRecoilState({}); // $ExpectError useResetRecoilState(myAtom); useResetRecoilState(writeableSelector); -useResetRecoilState(readOnlySelector); // $ExpectError +useResetRecoilState(readOnlySelectorSel); // $ExpectError useResetRecoilState({}); // $ExpectError useRecoilCallback(async ({ getPromise, getLoadable, set, reset }) => { @@ -138,10 +152,155 @@ isRecoilValue(myAtom); isRecoilValue(null); isRecoilValue(mySelector1); -// UTILS +/** + * ================ UTILS ================ + */ -// atomFamily -// atomFamily({ -// key: 'asdfs', -// default: (a) => {return ''}, -// }); +/** + * atomFamily() tests + */ + +{ + const myAtomFam = atomFamily({ + key: 'myAtomFam1', + default: (param: number) => param, + }); + + const atm = myAtomFam(2); // $ExpectType RecoilState + useRecoilValue(atm); // $ExpectType number + + myAtomFam(''); // $ExpectError +} + +/** + * selectorFamily() tests + */ +{ + const mySelectorFam = selectorFamily({ + key: 'myAtomFam1', + get: (param: number) => ({ get }) => { + get(mySelector1); // $ExpectType number + + return param; + }, + }); + + const atm = mySelectorFam(2); // $ExpectType RecoilValueReadOnly + useRecoilValue(atm); // $ExpectType number + + mySelectorFam(''); // $ExpectError + + useRecoilState(mySelectorFam(3)); // $ExpectError + + const mySelectorFamWritable = selectorFamily({ + key: 'myAtomFam1', + get: (param: number) => ({ get }) => { + get(mySelector1); // $ExpectType number + + return param; + }, + set: (param: number) => () => {}, + }); + + useRecoilState(mySelectorFamWritable(3))[0]; // $ExpectType number +} + +/** + * constSelector() tests + */ +{ + const mySel = constSelector(1); + const mySel2 = constSelector('hello'); + const mySel3 = constSelector([1, 2]); + const mySel4 = constSelector({ a: 1, b: '2' }); + + useRecoilValue(mySel); // $ExpectType 1 + useRecoilValue(mySel2); // $ExpectType "hello" + useRecoilValue(mySel3); // $ExpectType number[] + useRecoilValue(mySel4); // $ExpectType { a: number; b: string; } + + constSelector(new Map()); // $ExpectError + constSelector(new Set()); // $ExpectError +} + +/** + * errorSelector() tests + */ +{ + const mySel = errorSelector('Error msg'); + + useRecoilValue(mySel); // $ExpectType never + + errorSelector(2); // $ExpectError + errorSelector({}); // $ExpectError +} + +/** + * readOnlySelector() tests + */ +{ + const myWritableSel: RecoilState = {} as any; + + readOnlySelector(myWritableSel); // $ExpectType RecoilValueReadOnly +} + +/** + * noWait() tests + */ +{ + const numSel: RecoilValueReadOnly = {} as any; + const mySel = noWait(numSel); + + useRecoilValue(mySel); // $ExpectType Loadable +} + +/** + * waitForNone() tests + */ +{ + const numSel: RecoilValueReadOnly = {} as any; + const strSel: RecoilValueReadOnly = {} as any; + + const mySel = waitForNone([numSel, strSel]); + const mySel2 = waitForNone({ a: numSel, b: strSel }); + + useRecoilValue(mySel)[0]; // $ExpectType Loadable + useRecoilValue(mySel)[1]; // $ExpectType Loadable + + useRecoilValue(mySel2).a; // $ExpectType Loadable + useRecoilValue(mySel2).b; // $ExpectType Loadable +} + +/** + * waitForAny() tests + */ +{ + const numSel: RecoilValueReadOnly = {} as any; + const strSel: RecoilValueReadOnly = {} as any; + + const mySel = waitForAny([numSel, strSel]); + const mySel2 = waitForAny({ a: numSel, b: strSel }); + + useRecoilValue(mySel)[0]; // $ExpectType Loadable + useRecoilValue(mySel)[1]; // $ExpectType Loadable + + useRecoilValue(mySel2).a; // $ExpectType Loadable + useRecoilValue(mySel2).b; // $ExpectType Loadable +} + +/** + * waitForAll() tests + */ +{ + const numSel: RecoilValueReadOnly = {} as any; + const strSel: RecoilValueReadOnly = {} as any; + + const mySel = waitForAll([numSel, strSel]); + const mySel2 = waitForAll({ a: numSel, b: strSel }); + + useRecoilValue(mySel)[0]; // $ExpectType Loadable + useRecoilValue(mySel)[1]; // $ExpectType Loadable + + useRecoilValue(mySel2).a; // $ExpectType Loadable + useRecoilValue(mySel2).b; // $ExpectType Loadable +}