Skip to content

Commit

Permalink
Recoil utility types (DefinitelyTyped#45206)
Browse files Browse the repository at this point in the history
  • Loading branch information
csantos42 committed Jun 12, 2020
1 parent dd70728 commit 07d88fb
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 16 deletions.
89 changes: 89 additions & 0 deletions types/recoil/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,92 @@ export class RecoilValueReadOnly<T> extends AbstractRecoilValueReadonly<T> {}
export type RecoilValue<T> = RecoilValueReadOnly<T> | RecoilState<T>;

export function isRecoilValue(val: unknown): val is RecoilValue<any>;

/** 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<T, P extends SerializableParam> {
key: NodeKey;
dangerouslyAllowMutability?: boolean;
default: RecoilValue<T> | Promise<T> | T | ((param: P) => T | RecoilValue<T> | Promise<T>);
}

export function atomFamily<T, P extends SerializableParam>(
options: AtomFamilyOptions<T, P>,
): (param: P) => RecoilState<T>;

export interface ReadOnlySelectorFamilyOptions<T, P extends SerializableParam> {
key: string;
get: (param: P) => (opts: { get: GetRecoilValue }) => Promise<T> | RecoilValue<T> | T;
// cacheImplementation_UNSTABLE?: () => CacheImplementation<Loadable<T>>,
// cacheImplementationForParams_UNSTABLE?: () => CacheImplementation<
// RecoilValue<T>,
// >,
dangerouslyAllowMutability?: boolean;
}

export interface ReadWriteSelectorFamilyOptions<T, P extends SerializableParam> {
key: string;
get: (param: P) => (opts: { get: GetRecoilValue }) => Promise<T> | RecoilValue<T> | T;
set: (
param: P,
) => (
opts: { set: SetRecoilState; get: GetRecoilValue; reset: ResetRecoilState },
newValue: T | DefaultValue,
) => void;
// cacheImplementation_UNSTABLE?: () => CacheImplementation<Loadable<T>>,
// cacheImplementationForParams_UNSTABLE?: () => CacheImplementation<
// RecoilValue<T>,
// >,
dangerouslyAllowMutability?: boolean;
}

export function selectorFamily<T, P extends SerializableParam>(
options: ReadWriteSelectorFamilyOptions<T, P>,
): (param: P) => RecoilState<T>;

export function selectorFamily<T, P extends SerializableParam>(
options: ReadOnlySelectorFamilyOptions<T, P>,
): (param: P) => RecoilValueReadOnly<T>;

export function constSelector<T extends SerializableParam>(constant: T): RecoilValueReadOnly<T>;

export function errorSelector(message: string): RecoilValueReadOnly<never>;

export function readOnlySelector<T>(atom: RecoilValue<T>): RecoilValueReadOnly<T>;

export function noWait<T>(state: RecoilValue<T>): RecoilValueReadOnly<Loadable<T>>;

export type UnwrapRecoilValue<T> = T extends RecoilValue<infer R> ? R : never;

export type UnwrapRecoilValueLoadables<T extends Array<RecoilValue<any>> | { [key: string]: RecoilValue<any> }> = {
[P in keyof T]: Loadable<UnwrapRecoilValue<T[P]>>;
};

export function waitForNone<RecoilValues extends Array<RecoilValue<any>> | [RecoilValue<any>]>(
param: RecoilValues,
): RecoilValueReadOnly<UnwrapRecoilValueLoadables<RecoilValues>>;

export function waitForNone<RecoilValues extends { [key: string]: RecoilValue<any> }>(
param: RecoilValues,
): RecoilValueReadOnly<UnwrapRecoilValueLoadables<RecoilValues>>;

export function waitForAny<RecoilValues extends Array<RecoilValue<any>> | [RecoilValue<any>]>(
param: RecoilValues,
): RecoilValueReadOnly<UnwrapRecoilValueLoadables<RecoilValues>>;

export function waitForAny<RecoilValues extends { [key: string]: RecoilValue<any> }>(
param: RecoilValues,
): RecoilValueReadOnly<UnwrapRecoilValueLoadables<RecoilValues>>;

export function waitForAll<RecoilValues extends Array<RecoilValue<any>> | [RecoilValue<any>]>(
param: RecoilValues,
): RecoilValueReadOnly<UnwrapRecoilValueLoadables<RecoilValues>>;

export function waitForAll<RecoilValues extends { [key: string]: RecoilValue<any> }>(
param: RecoilValues,
): RecoilValueReadOnly<UnwrapRecoilValueLoadables<RecoilValues>>;
191 changes: 175 additions & 16 deletions types/recoil/recoil-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ import {
useRecoilCallback,
isRecoilValue,
RecoilState,
atomFamily,
selectorFamily,
constSelector,
errorSelector,
readOnlySelector,
noWait,
waitForNone,
waitForAny,
waitForAll,
} from 'recoil';
// import { atomFamily } from 'recoil/utils';

Expand All @@ -31,13 +40,18 @@ const mySelector1 = selector({
get: () => 5,
});

const mySelector2 = selector({
key: 'asds',
get: () => '',
});

// $ExpectError
selector({
key: 'asdfasfds',
get: () => '',
}) as RecoilValueReadOnly<boolean>;

const readOnlySelector = selector({
const readOnlySelectorSel = selector({
key: 'asdfasf',
get: ({ get }) => {
get(myAtom) + 10;
Expand All @@ -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
},
});

Expand All @@ -68,7 +82,7 @@ RecoilRoot({
set(myAtom, 5);
setUnvalidatedAtomValues(new Map());

set(readOnlySelector, 2); // $ExpectError
set(readOnlySelectorSel, 2); // $ExpectError
setUnvalidatedAtomValues({}); // $ExpectError
},
});
Expand All @@ -92,33 +106,33 @@ useRecoilValue<number>(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 }) => {
Expand All @@ -138,10 +152,155 @@ isRecoilValue(myAtom);
isRecoilValue(null);
isRecoilValue(mySelector1);

// UTILS
/**
* ================ UTILS ================
*/

/**
* atomFamily() tests
*/

{
const myAtomFam = atomFamily({
key: 'myAtomFam1',
default: (param: number) => param,
});

const atm = myAtomFam(2); // $ExpectType RecoilState<number>
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<number>
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<number> = {} as any;

readOnlySelector(myWritableSel); // $ExpectType RecoilValueReadOnly<number>
}

/**
* noWait() tests
*/
{
const numSel: RecoilValueReadOnly<number> = {} as any;
const mySel = noWait(numSel);

useRecoilValue(mySel); // $ExpectType Loadable<number>
}

/**
* waitForNone() tests
*/
{
const numSel: RecoilValueReadOnly<number> = {} as any;
const strSel: RecoilValueReadOnly<string> = {} as any;

const mySel = waitForNone([numSel, strSel]);
const mySel2 = waitForNone({ a: numSel, b: strSel });

useRecoilValue(mySel)[0]; // $ExpectType Loadable<number>
useRecoilValue(mySel)[1]; // $ExpectType Loadable<string>

useRecoilValue(mySel2).a; // $ExpectType Loadable<number>
useRecoilValue(mySel2).b; // $ExpectType Loadable<string>
}

/**
* waitForAny() tests
*/
{
const numSel: RecoilValueReadOnly<number> = {} as any;
const strSel: RecoilValueReadOnly<string> = {} as any;

const mySel = waitForAny([numSel, strSel]);
const mySel2 = waitForAny({ a: numSel, b: strSel });

useRecoilValue(mySel)[0]; // $ExpectType Loadable<number>
useRecoilValue(mySel)[1]; // $ExpectType Loadable<string>

useRecoilValue(mySel2).a; // $ExpectType Loadable<number>
useRecoilValue(mySel2).b; // $ExpectType Loadable<string>
}

/**
* waitForAll() tests
*/
{
const numSel: RecoilValueReadOnly<number> = {} as any;
const strSel: RecoilValueReadOnly<string> = {} as any;

const mySel = waitForAll([numSel, strSel]);
const mySel2 = waitForAll({ a: numSel, b: strSel });

useRecoilValue(mySel)[0]; // $ExpectType Loadable<number>
useRecoilValue(mySel)[1]; // $ExpectType Loadable<string>

// atomFamily
// atomFamily({
// key: 'asdfs',
// default: (a) => {return ''},
// });
useRecoilValue(mySel2).a; // $ExpectType Loadable<number>
useRecoilValue(mySel2).b; // $ExpectType Loadable<string>
}

0 comments on commit 07d88fb

Please sign in to comment.