Merge pull request #33220 from Jessidhia/useref-undefined-overload

[react] Add some support for special cases using undefined with hooks
This commit is contained in:
Jessica Franco 2019-02-20 17:24:30 +09:00 committed by GitHub
commit 9e673d252d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 4 deletions

View File

@ -807,6 +807,14 @@ declare namespace React {
* @see https://reactjs.org/docs/hooks-reference.html#usestate
*/
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
// convenience overload when first argument is ommitted
/**
* Returns a stateful value, and a function to update it.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usestate
*/
function useState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
/**
* An alternative to `useState`.
*
@ -894,6 +902,20 @@ declare namespace React {
*/
// TODO (TypeScript 3.0): <T extends unknown>
function useRef<T>(initialValue: T|null): RefObject<T>;
// convenience overload for potentially undefined initialValue / call with 0 arguments
// has a default to stop it from defaulting to {} instead
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. Its handy for keeping any mutable
* value around similar to how youd use instance fields in classes.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
// TODO (TypeScript 3.0): <T extends unknown>
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
/**
* The signature is identical to `useEffect`, but it fires synchronously after all DOM mutations.
* Use this to read layout from the DOM and synchronously re-render. Updates scheduled inside
@ -958,7 +980,8 @@ declare namespace React {
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usememo
*/
function useMemo<T>(factory: () => T, deps: DependencyList): T;
// allow undefined, but don't make it optional as that is very likely a mistake
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
/**
* `useDebugValue` can be used to display a label for custom hooks in React DevTools.
*

View File

@ -100,9 +100,46 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean {
// inline object, to (manually) check if autocomplete works
React.useReducer(reducer, { age: 42, name: 'The Answer' });
// make sure this is not going to the |null overload
// $ExpectType MutableRefObject<boolean>
const didLayout = React.useRef(false);
// test useRef and its convenience overloads
// $ExpectType MutableRefObject<number>
React.useRef(0);
// these are not very useful (can't assign anything else to .current)
// but it's the only safe way to resolve them
// $ExpectType MutableRefObject<null>
React.useRef(null);
// $ExpectType MutableRefObject<undefined>
React.useRef(undefined);
// |null convenience overload
// it should _not_ be mutable if the generic argument doesn't include null
// $ExpectType RefObject<number>
React.useRef<number>(null);
// but it should be mutable if it does (i.e. is not the convenience overload)
// $ExpectType MutableRefObject<number | null>
React.useRef<number | null>(null);
// |undefined convenience overload
// with no contextual type or generic argument it should default to undefined only (not {} or unknown!)
// $ExpectType MutableRefObject<undefined>
React.useRef();
// $ExpectType MutableRefObject<number | undefined>
React.useRef<number>();
// don't just accept a potential undefined if there is a generic argument
// $ExpectError
React.useRef<number>(undefined);
// make sure once again there's no |undefined if the initial value doesn't either
// $ExpectType MutableRefObject<number>
React.useRef<number>(1);
// and also that it is not getting erased if the parameter is wider
// $ExpectType MutableRefObject<number | undefined>
React.useRef<number | undefined>(1);
// should be contextually typed
const a: React.MutableRefObject<number | undefined> = React.useRef(undefined);
const b: React.MutableRefObject<number | undefined> = React.useRef();
const c: React.MutableRefObject<number | null> = React.useRef(null);
const d: React.RefObject<number> = React.useRef(null);
const id = React.useMemo(() => Math.random(), []);
React.useImperativeHandle(ref, () => ({ id }), [id]);
@ -110,6 +147,10 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean {
// $ExpectError
React.useImperativeMethods(ref, () => ({}), [id]);
// make sure again this is not going to the |null convenience overload
// $ExpectType MutableRefObject<boolean>
const didLayout = React.useRef(false);
React.useLayoutEffect(() => {
setState(1);
setState(prevState => prevState - 1);
@ -140,6 +181,29 @@ function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean {
React.useDebugValue(id, value => value.toFixed());
React.useDebugValue(id);
// allow passing an explicit undefined
React.useMemo(() => {}, undefined);
// but don't allow it to be missing
// $ExpectError
React.useMemo(() => {});
// useState convenience overload
// default to undefined only (not that useful, but type-safe -- no {} or unknown!)
// $ExpectType undefined
React.useState()[0];
// $ExpectType number | undefined
React.useState<number>()[0];
// default overload
// $ExpectType number
React.useState(0)[0];
// $ExpectType undefined
React.useState(undefined)[0];
// make sure the generic argument does reject actual potentially undefined inputs
// $ExpectError
React.useState<number>(undefined)[0];
// useReducer convenience overload
return React.useCallback(() => didLayout.current, []);
}