From 9050d36d5763beabb1a642819dd24d61cccb3ffe Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Tue, 17 Mar 2020 23:28:07 +1100 Subject: [PATCH] std: Provide types for React and ReactDOM (#4376) Introduces `std/types` which is designed to provide types for common libraries that are compatible with Deno. --- std/types/README.md | 28 + std/types/react-dom.d.ts | 125 + std/types/react.d.ts | 3843 ++++ std/types/react/csstype.d.ts | 33062 +++++++++++++++++++++++++++ std/types/react/prop-types.d.ts | 123 + std/types/react/react_global.d.ts | 149 + std/types/tests/react-dom_mock.js | 9 + std/types/tests/react-dom_test.tsx | 20 + std/types/tests/react_mock.js | 9 + std/types/tests/react_test.tsx | 23 + 10 files changed, 37391 insertions(+) create mode 100644 std/types/README.md create mode 100644 std/types/react-dom.d.ts create mode 100644 std/types/react.d.ts create mode 100644 std/types/react/csstype.d.ts create mode 100644 std/types/react/prop-types.d.ts create mode 100644 std/types/react/react_global.d.ts create mode 100644 std/types/tests/react-dom_mock.js create mode 100644 std/types/tests/react-dom_test.tsx create mode 100644 std/types/tests/react_mock.js create mode 100644 std/types/tests/react_test.tsx diff --git a/std/types/README.md b/std/types/README.md new file mode 100644 index 0000000000..86fa40a2d4 --- /dev/null +++ b/std/types/README.md @@ -0,0 +1,28 @@ +# std/types + +Contains types for popular external packages that are compatible with Deno. + +Because Deno only resolves fully qualified file names, type definitions that +import other type definitions might not work with Deno. Also, when some type +definition supply some global interfaces, they can conflict with Deno. The types +located here have been validated to work with Deno. + +The types that are currently available: + +- `react.d.ts` - For React 16. Sources known to work well for Deno: + - Pika CDN: `https://cdn.pika.dev/_/react/v16` + - JSPM: `https://dev.jspm.io/react@16` +- `react-dom.d.ts` - For ReactDOM 16. Sources known to work well for Deno: + - Pika CDN: `https://cdn.pika.dev/_/react-dom/v16` + - JSPM: `https://dev.jspm.io/react-dom@16` + +There are several ways these type definitions can be referenced. Likely the +"best" way is that the CDN provider provides a header of `X-TypeScript-Types` +which points to the type definitions. We are working to have this available, but +currently you would need to use the compiler hint of `@deno-types`. For example +to import React: + +```ts +// @deno-types="https://deno.land/std/types/react.d.ts" +import React from "https://cdn.pika.dev/_/react/v16"; +``` diff --git a/std/types/react-dom.d.ts b/std/types/react-dom.d.ts new file mode 100644 index 0000000000..f135197f2b --- /dev/null +++ b/std/types/react-dom.d.ts @@ -0,0 +1,125 @@ +// These types are adapted from +// https://github.com/DefinitelyTyped/DefinitelyTyped to work under Deno. +// +// Type definitions for React (react-dom) 16.9 +// Project: http://facebook.github.io/react/ +// Definitions by: Asana +// AssureSign +// Microsoft +// MartynasZilinskas +// Josh Rutherford +// Jessica Franco +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +// NOTE: Users of the `experimental` builds of React should add a reference +// to 'react-dom/experimental' in their project. See experimental.d.ts's top comment +// for reference and documentation on how exactly to do it. + +/* eslint-disable */ + +export as namespace ReactDOM; + +import { + ReactInstance, + Component, + ComponentState, + ReactElement, + SFCElement, + CElement, + DOMAttributes, + DOMElement, + ReactNode, + ReactPortal +} from "./react.d.ts"; + +export function findDOMNode( + instance: ReactInstance | null | undefined +): Element | null | Text; +export function unmountComponentAtNode(container: Element): boolean; + +export function createPortal( + children: ReactNode, + container: Element, + key?: null | string +): ReactPortal; + +export const version: string; +export const render: Renderer; +export const hydrate: Renderer; + +export function unstable_batchedUpdates( + callback: (a: A, b: B) => any, + a: A, + b: B +): void; +export function unstable_batchedUpdates(callback: (a: A) => any, a: A): void; +export function unstable_batchedUpdates(callback: () => any): void; + +export function unstable_renderSubtreeIntoContainer( + parentComponent: Component, + element: DOMElement, T>, + container: Element, + callback?: (element: T) => any +): T; +export function unstable_renderSubtreeIntoContainer< + P, + T extends Component +>( + parentComponent: Component, + element: CElement, + container: Element, + callback?: (component: T) => any +): T; +export function unstable_renderSubtreeIntoContainer

( + parentComponent: Component, + element: ReactElement

, + container: Element, + callback?: (component?: Component | Element) => any +): Component | Element | void; + +export interface Renderer { + // Deprecated(render): The return value is deprecated. + // In future releases the render function's return type will be void. + + ( + element: DOMElement, T>, + container: Element | null, + callback?: () => void + ): T; + + ( + element: Array, any>>, + container: Element | null, + callback?: () => void + ): Element; + + ( + element: SFCElement | Array>, + container: Element | null, + callback?: () => void + ): void; + + >( + element: CElement, + container: Element | null, + callback?: () => void + ): T; + + ( + element: Array>>, + container: Element | null, + callback?: () => void + ): Component; + +

( + element: ReactElement

, + container: Element | null, + callback?: () => void + ): Component | Element | void; + + (element: ReactElement[], container: Element | null, callback?: () => void): + | Component + | Element + | void; +} diff --git a/std/types/react.d.ts b/std/types/react.d.ts new file mode 100644 index 0000000000..b512a43a29 --- /dev/null +++ b/std/types/react.d.ts @@ -0,0 +1,3843 @@ +// These types are adapted from +// https://github.com/DefinitelyTyped/DefinitelyTyped to work under Deno. +// +// Type definitions for React 16.9 +// Project: http://facebook.github.io/react/ +// Definitions by: Asana +// AssureSign +// Microsoft +// John Reilly +// Benoit Benezech +// Patricio Zavolinsky +// Digiguru +// Eric Anderson +// Dovydas Navickas +// Josh Rutherford +// Guilherme Hübner +// Ferdy Budhidharma +// Johann Rakotoharisoa +// Olivier Pascal +// Martin Hochel +// Frank Li +// Jessica Franco +// Saransh Kataria +// Kanitkorn Sujautra +// Sebastian Silbermann +// Kyle Scully +// Cong Zhang +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/* eslint-disable */ + +import "./react/react_global.d.ts"; + +import * as CSS from "./react/csstype.d.ts"; +import * as PropTypes from "./react/prop-types.d.ts"; + +type NativeAnimationEvent = AnimationEvent; +type NativeClipboardEvent = ClipboardEvent; +type NativeCompositionEvent = CompositionEvent; +type NativeDragEvent = DragEvent; +type NativeFocusEvent = FocusEvent; +type NativeKeyboardEvent = KeyboardEvent; +type NativeMouseEvent = MouseEvent; +type NativeTouchEvent = TouchEvent; +type NativePointerEvent = PointerEvent; +type NativeTransitionEvent = TransitionEvent; +type NativeUIEvent = UIEvent; +type NativeWheelEvent = WheelEvent; +type Booleanish = boolean | "true" | "false"; + +/** + * defined in scheduler/tracing + */ +interface SchedulerInteraction { + id: number; + name: string; + timestamp: number; +} + +// tslint:disable-next-line:export-just-namespace +export = React; +export as namespace React; + +declare namespace React { + // + // React Elements + // ---------------------------------------------------------------------- + + type ElementType

= + | { + [K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] + ? K + : never; + }[keyof JSX.IntrinsicElements] + | ComponentType

; + /** + * @deprecated Please use `ElementType` + */ + type ReactType

= ElementType

; + type ComponentType

= ComponentClass

| FunctionComponent

; + + type JSXElementConstructor

= + | ((props: P) => ReactElement | null) + | (new (props: P) => Component); + + type Key = string | number; + + interface RefObject { + readonly current: T | null; + } + + type Ref = + | { bivarianceHack(instance: T | null): void }["bivarianceHack"] + | RefObject + | null; + type LegacyRef = string | Ref; + + type ComponentState = any; + + /** + * @internal You shouldn't need to use this type since you never see these attributes + * inside your component or have to validate them. + */ + interface Attributes { + key?: Key; + } + interface RefAttributes extends Attributes { + ref?: Ref; + } + interface ClassAttributes extends Attributes { + ref?: LegacyRef; + } + + interface ReactElement< + P = any, + T extends string | JSXElementConstructor = + | string + | JSXElementConstructor + > { + type: T; + props: P; + key: Key | null; + } + + interface ReactComponentElement< + T extends keyof JSX.IntrinsicElements | JSXElementConstructor, + P = Pick, Exclude, "key" | "ref">> + > extends ReactElement {} + + /** + * @deprecated Please use `FunctionComponentElement` + */ + type SFCElement

= FunctionComponentElement

; + + interface FunctionComponentElement

+ extends ReactElement> { + ref?: "ref" extends keyof P + ? P extends { ref?: infer R } + ? R + : never + : never; + } + + type CElement> = ComponentElement< + P, + T + >; + interface ComponentElement> + extends ReactElement> { + ref?: LegacyRef; + } + + type ClassicElement

= CElement>; + + // string fallback for custom web-components + interface DOMElement< + P extends HTMLAttributes | SVGAttributes, + T extends Element + > extends ReactElement { + ref: LegacyRef; + } + + // ReactHTML for ReactHTMLElement + // tslint:disable-next-line:no-empty-interface + interface ReactHTMLElement + extends DetailedReactHTMLElement, T> {} + + interface DetailedReactHTMLElement< + P extends HTMLAttributes, + T extends HTMLElement + > extends DOMElement { + type: keyof ReactHTML; + } + + // ReactSVG for ReactSVGElement + interface ReactSVGElement + extends DOMElement, SVGElement> { + type: keyof ReactSVG; + } + + interface ReactPortal extends ReactElement { + key: Key | null; + children: ReactNode; + } + + // + // Factories + // ---------------------------------------------------------------------- + + type Factory

= ( + props?: Attributes & P, + ...children: ReactNode[] + ) => ReactElement

; + + /** + * @deprecated Please use `FunctionComponentFactory` + */ + type SFCFactory

= FunctionComponentFactory

; + + type FunctionComponentFactory

= ( + props?: Attributes & P, + ...children: ReactNode[] + ) => FunctionComponentElement

; + + type ComponentFactory> = ( + props?: ClassAttributes & P, + ...children: ReactNode[] + ) => CElement; + + type CFactory> = ComponentFactory< + P, + T + >; + type ClassicFactory

= CFactory>; + + type DOMFactory

, T extends Element> = ( + props?: (ClassAttributes & P) | null, + ...children: ReactNode[] + ) => DOMElement; + + // tslint:disable-next-line:no-empty-interface + interface HTMLFactory + extends DetailedHTMLFactory, T> {} + + interface DetailedHTMLFactory< + P extends HTMLAttributes, + T extends HTMLElement + > extends DOMFactory { + ( + props?: (ClassAttributes & P) | null, + ...children: ReactNode[] + ): DetailedReactHTMLElement; + } + + interface SVGFactory + extends DOMFactory, SVGElement> { + ( + props?: (ClassAttributes & SVGAttributes) | null, + ...children: ReactNode[] + ): ReactSVGElement; + } + + // + // React Nodes + // http://facebook.github.io/react/docs/glossary.html + // ---------------------------------------------------------------------- + + type ReactText = string | number; + type ReactChild = ReactElement | ReactText; + + interface ReactNodeArray extends Array {} + type ReactFragment = {} | ReactNodeArray; + type ReactNode = + | ReactChild + | ReactFragment + | ReactPortal + | boolean + | null + | undefined; + + // + // Top Level API + // ---------------------------------------------------------------------- + + // DOM Elements + function createFactory( + type: keyof ReactHTML + ): HTMLFactory; + function createFactory(type: keyof ReactSVG): SVGFactory; + function createFactory

, T extends Element>( + type: string + ): DOMFactory; + + // Custom components + function createFactory

( + type: FunctionComponent

+ ): FunctionComponentFactory

; + function createFactory

( + type: ClassType< + P, + ClassicComponent, + ClassicComponentClass

+ > + ): CFactory>; + function createFactory< + P, + T extends Component, + C extends ComponentClass

+ >(type: ClassType): CFactory; + function createFactory

(type: ComponentClass

): Factory

; + + // DOM Elements + // TODO: generalize this to everything in `keyof ReactHTML`, not just "input" + function createElement( + type: "input", + props?: + | (InputHTMLAttributes & + ClassAttributes) + | null, + ...children: ReactNode[] + ): DetailedReactHTMLElement< + InputHTMLAttributes, + HTMLInputElement + >; + function createElement

, T extends HTMLElement>( + type: keyof ReactHTML, + props?: (ClassAttributes & P) | null, + ...children: ReactNode[] + ): DetailedReactHTMLElement; + function createElement

, T extends SVGElement>( + type: keyof ReactSVG, + props?: (ClassAttributes & P) | null, + ...children: ReactNode[] + ): ReactSVGElement; + function createElement

, T extends Element>( + type: string, + props?: (ClassAttributes & P) | null, + ...children: ReactNode[] + ): DOMElement; + + // Custom components + + function createElement

( + type: FunctionComponent

, + props?: (Attributes & P) | null, + ...children: ReactNode[] + ): FunctionComponentElement

; + function createElement

( + type: ClassType< + P, + ClassicComponent, + ClassicComponentClass

+ >, + props?: (ClassAttributes> & P) | null, + ...children: ReactNode[] + ): CElement>; + function createElement< + P extends {}, + T extends Component, + C extends ComponentClass

+ >( + type: ClassType, + props?: (ClassAttributes & P) | null, + ...children: ReactNode[] + ): CElement; + function createElement

( + type: FunctionComponent

| ComponentClass

| string, + props?: (Attributes & P) | null, + ...children: ReactNode[] + ): ReactElement

; + + // DOM Elements + // ReactHTMLElement + function cloneElement

, T extends HTMLElement>( + element: DetailedReactHTMLElement, + props?: P, + ...children: ReactNode[] + ): DetailedReactHTMLElement; + // ReactHTMLElement, less specific + function cloneElement

, T extends HTMLElement>( + element: ReactHTMLElement, + props?: P, + ...children: ReactNode[] + ): ReactHTMLElement; + // SVGElement + function cloneElement

, T extends SVGElement>( + element: ReactSVGElement, + props?: P, + ...children: ReactNode[] + ): ReactSVGElement; + // DOM Element (has to be the last, because type checking stops at first overload that fits) + function cloneElement

, T extends Element>( + element: DOMElement, + props?: DOMAttributes & P, + ...children: ReactNode[] + ): DOMElement; + + // Custom components + function cloneElement

( + element: FunctionComponentElement

, + props?: Partial

& Attributes, + ...children: ReactNode[] + ): FunctionComponentElement

; + function cloneElement>( + element: CElement, + props?: Partial

& ClassAttributes, + ...children: ReactNode[] + ): CElement; + function cloneElement

( + element: ReactElement

, + props?: Partial

& Attributes, + ...children: ReactNode[] + ): ReactElement

; + + // Context via RenderProps + interface ProviderProps { + value: T; + children?: ReactNode; + } + + interface ConsumerProps { + children: (value: T) => ReactNode; + unstable_observedBits?: number; + } + + // TODO: similar to how Fragment is actually a symbol, the values returned from createContext, + // forwardRef and memo are actually objects that are treated specially by the renderer; see: + // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/ReactContext.js#L35-L48 + // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/forwardRef.js#L42-L45 + // https://github.com/facebook/react/blob/v16.6.0/packages/react/src/memo.js#L27-L31 + // However, we have no way of telling the JSX parser that it's a JSX element type or its props other than + // by pretending to be a normal component. + // + // We don't just use ComponentType or SFC types because you are not supposed to attach statics to this + // object, but rather to the original function. + interface ExoticComponent

{ + /** + * **NOTE**: Exotic components are not callable. + */ + (props: P): ReactElement | null; + readonly $$typeof: symbol; + } + + interface NamedExoticComponent

extends ExoticComponent

{ + displayName?: string; + } + + interface ProviderExoticComponent

extends ExoticComponent

{ + propTypes?: WeakValidationMap

; + } + + type ContextType> = C extends Context + ? T + : never; + + // NOTE: only the Context object itself can get a displayName + // https://github.com/facebook/react-devtools/blob/e0b854e4c/backend/attachRendererFiber.js#L310-L325 + type Provider = ProviderExoticComponent>; + type Consumer = ExoticComponent>; + interface Context { + Provider: Provider; + Consumer: Consumer; + displayName?: string; + } + function createContext( + // If you thought this should be optional, see + // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24509#issuecomment-382213106 + defaultValue: T, + calculateChangedBits?: (prev: T, next: T) => number + ): Context; + + function isValidElement

( + object: {} | null | undefined + ): object is ReactElement

; + + const Children: ReactChildren; + const Fragment: ExoticComponent<{ children?: ReactNode }>; + const StrictMode: ExoticComponent<{ children?: ReactNode }>; + + interface SuspenseProps { + children?: ReactNode; + + /** A fallback react tree to show when a Suspense child (like React.lazy) suspends */ + fallback: NonNullable | null; + /** + * Tells React whether to “skip” revealing this boundary during the initial load. + * This API will likely be removed in a future release. + */ + // NOTE: this is unflagged and is respected even in stable builds + unstable_avoidThisFallback?: boolean; + } + /** + * This feature is not yet available for server-side rendering. + * Suspense support will be added in a later release. + */ + const Suspense: ExoticComponent; + const version: string; + + /** + * {@link https://github.com/bvaughn/rfcs/blob/profiler/text/0000-profiler.md#detailed-design | API} + */ + type ProfilerOnRenderCallback = ( + id: string, + phase: "mount" | "update", + actualDuration: number, + baseDuration: number, + startTime: number, + commitTime: number, + interactions: Set + ) => void; + interface ProfilerProps { + children?: ReactNode; + id: string; + onRender: ProfilerOnRenderCallback; + } + + const Profiler: ExoticComponent; + + // + // Component API + // ---------------------------------------------------------------------- + + type ReactInstance = Component | Element; + + // Base component for plain JS classes + // tslint:disable-next-line:no-empty-interface + interface Component

+ extends ComponentLifecycle {} + class Component { + // tslint won't let me format the sample code in a way that vscode likes it :( + /** + * If set, `this.context` will be set at runtime to the current value of the given Context. + * + * Usage: + * + * ```ts + * type MyContext = number + * const Ctx = React.createContext(0) + * + * class Foo extends React.Component { + * static contextType = Ctx + * context!: React.ContextType + * render () { + * return <>My context's value: {this.context}; + * } + * } + * ``` + * + * @see https://reactjs.org/docs/context.html#classcontexttype + */ + static contextType?: Context; + + /** + * If using the new style context, re-declare this in your class to be the + * `React.ContextType` of your `static contextType`. + * Should be used with type annotation or static contextType. + * + * ```ts + * static contextType = MyContext + * // For TS pre-3.7: + * context!: React.ContextType + * // For TS 3.7 and above: + * declare context: React.ContextType + * ``` + * + * @see https://reactjs.org/docs/context.html + */ + // TODO (TypeScript 3.0): unknown + context: any; + + constructor(props: Readonly

); + /** + * @deprecated + * @see https://reactjs.org/docs/legacy-context.html + */ + constructor(props: P, context?: any); + + // We MUST keep setState() as a unified signature because it allows proper checking of the method return type. + // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257 + // Also, the ` | S` allows intellisense to not be dumbisense + setState( + state: + | (( + prevState: Readonly, + props: Readonly

+ ) => Pick | S | null) + | (Pick | S | null), + callback?: () => void + ): void; + + forceUpdate(callback?: () => void): void; + render(): ReactNode; + + // React.Props is now deprecated, which means that the `children` + // property is not available on `P` by default, even though you can + // always pass children as variadic arguments to `createElement`. + // In the future, if we can define its call signature conditionally + // on the existence of `children` in `P`, then we should remove this. + readonly props: Readonly

& Readonly<{ children?: ReactNode }>; + state: Readonly; + /** + * @deprecated + * https://reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs + */ + refs: { + [key: string]: ReactInstance; + }; + } + + class PureComponent

extends Component {} + + interface ClassicComponent

extends Component { + replaceState(nextState: S, callback?: () => void): void; + isMounted(): boolean; + getInitialState?(): S; + } + + interface ChildContextProvider { + getChildContext(): CC; + } + + // + // Class Interfaces + // ---------------------------------------------------------------------- + + /** + * @deprecated as of recent React versions, function components can no + * longer be considered 'stateless'. Please use `FunctionComponent` instead. + * + * @see [React Hooks](https://reactjs.org/docs/hooks-intro.html) + */ + type SFC

= FunctionComponent

; + + /** + * @deprecated as of recent React versions, function components can no + * longer be considered 'stateless'. Please use `FunctionComponent` instead. + * + * @see [React Hooks](https://reactjs.org/docs/hooks-intro.html) + */ + type StatelessComponent

= FunctionComponent

; + + type FC

= FunctionComponent

; + + interface FunctionComponent

{ + (props: PropsWithChildren

, context?: any): ReactElement | null; + propTypes?: WeakValidationMap

; + contextTypes?: ValidationMap; + defaultProps?: Partial

; + displayName?: string; + } + + interface RefForwardingComponent { + (props: PropsWithChildren

, ref: Ref): ReactElement | null; + propTypes?: WeakValidationMap

; + contextTypes?: ValidationMap; + defaultProps?: Partial

; + displayName?: string; + } + + interface ComponentClass

+ extends StaticLifecycle { + new (props: P, context?: any): Component; + propTypes?: WeakValidationMap

; + contextType?: Context; + contextTypes?: ValidationMap; + childContextTypes?: ValidationMap; + defaultProps?: Partial

; + displayName?: string; + } + + interface ClassicComponentClass

extends ComponentClass

{ + new (props: P, context?: any): ClassicComponent; + getDefaultProps?(): P; + } + + /** + * We use an intersection type to infer multiple type parameters from + * a single argument, which is useful for many top-level API defs. + * See https://github.com/Microsoft/TypeScript/issues/7234 for more info. + */ + type ClassType< + P, + T extends Component, + C extends ComponentClass

+ > = C & (new (props: P, context?: any) => T); + + // + // Component Specs and Lifecycle + // ---------------------------------------------------------------------- + + // This should actually be something like `Lifecycle | DeprecatedLifecycle`, + // as React will _not_ call the deprecated lifecycle methods if any of the new lifecycle + // methods are present. + interface ComponentLifecycle + extends NewLifecycle, + DeprecatedLifecycle { + /** + * Called immediately after a component is mounted. Setting state here will trigger re-rendering. + */ + componentDidMount?(): void; + /** + * Called to determine whether the change in props and state should trigger a re-render. + * + * `Component` always returns true. + * `PureComponent` implements a shallow comparison on props and state and returns true if any + * props or states have changed. + * + * If false is returned, `Component#render`, `componentWillUpdate` + * and `componentDidUpdate` will not be called. + */ + shouldComponentUpdate?( + nextProps: Readonly

, + nextState: Readonly, + nextContext: any + ): boolean; + /** + * Called immediately before a component is destroyed. Perform any necessary cleanup in this method, such as + * cancelled network requests, or cleaning up any DOM elements created in `componentDidMount`. + */ + componentWillUnmount?(): void; + /** + * Catches exceptions generated in descendant components. Unhandled exceptions will cause + * the entire component tree to unmount. + */ + componentDidCatch?(error: Error, errorInfo: ErrorInfo): void; + } + + // Unfortunately, we have no way of declaring that the component constructor must implement this + interface StaticLifecycle { + getDerivedStateFromProps?: GetDerivedStateFromProps; + getDerivedStateFromError?: GetDerivedStateFromError; + } + + type GetDerivedStateFromProps = + /** + * Returns an update to a component's state based on its new props and old state. + * + * Note: its presence prevents any of the deprecated lifecycle methods from being invoked + */ + (nextProps: Readonly

, prevState: S) => Partial | null; + + type GetDerivedStateFromError = + /** + * This lifecycle is invoked after an error has been thrown by a descendant component. + * It receives the error that was thrown as a parameter and should return a value to update state. + * + * Note: its presence prevents any of the deprecated lifecycle methods from being invoked + */ + (error: any) => Partial | null; + + // This should be "infer SS" but can't use it yet + interface NewLifecycle { + /** + * Runs before React applies the result of `render` to the document, and + * returns an object to be given to componentDidUpdate. Useful for saving + * things such as scroll position before `render` causes changes to it. + * + * Note: the presence of getSnapshotBeforeUpdate prevents any of the deprecated + * lifecycle events from running. + */ + getSnapshotBeforeUpdate?( + prevProps: Readonly

, + prevState: Readonly + ): SS | null; + /** + * Called immediately after updating occurs. Not called for the initial render. + * + * The snapshot is only present if getSnapshotBeforeUpdate is present and returns non-null. + */ + componentDidUpdate?( + prevProps: Readonly

, + prevState: Readonly, + snapshot?: SS + ): void; + } + + interface DeprecatedLifecycle { + /** + * Called immediately before mounting occurs, and before `Component#render`. + * Avoid introducing any side-effects or subscriptions in this method. + * + * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps + * prevents this from being invoked. + * + * @deprecated 16.3, use componentDidMount or the constructor instead; will stop working in React 17 + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + */ + componentWillMount?(): void; + /** + * Called immediately before mounting occurs, and before `Component#render`. + * Avoid introducing any side-effects or subscriptions in this method. + * + * This method will not stop working in React 17. + * + * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps + * prevents this from being invoked. + * + * @deprecated 16.3, use componentDidMount or the constructor instead + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + */ + UNSAFE_componentWillMount?(): void; + /** + * Called when the component may be receiving new props. + * React may call this even if props have not changed, so be sure to compare new and existing + * props if you only want to handle changes. + * + * Calling `Component#setState` generally does not trigger this method. + * + * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps + * prevents this from being invoked. + * + * @deprecated 16.3, use static getDerivedStateFromProps instead; will stop working in React 17 + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + */ + componentWillReceiveProps?(nextProps: Readonly

, nextContext: any): void; + /** + * Called when the component may be receiving new props. + * React may call this even if props have not changed, so be sure to compare new and existing + * props if you only want to handle changes. + * + * Calling `Component#setState` generally does not trigger this method. + * + * This method will not stop working in React 17. + * + * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps + * prevents this from being invoked. + * + * @deprecated 16.3, use static getDerivedStateFromProps instead + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + */ + UNSAFE_componentWillReceiveProps?( + nextProps: Readonly

, + nextContext: any + ): void; + /** + * Called immediately before rendering when new props or state is received. Not called for the initial render. + * + * Note: You cannot call `Component#setState` here. + * + * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps + * prevents this from being invoked. + * + * @deprecated 16.3, use getSnapshotBeforeUpdate instead; will stop working in React 17 + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + */ + componentWillUpdate?( + nextProps: Readonly

, + nextState: Readonly, + nextContext: any + ): void; + /** + * Called immediately before rendering when new props or state is received. Not called for the initial render. + * + * Note: You cannot call `Component#setState` here. + * + * This method will not stop working in React 17. + * + * Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps + * prevents this from being invoked. + * + * @deprecated 16.3, use getSnapshotBeforeUpdate instead + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update + * @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path + */ + UNSAFE_componentWillUpdate?( + nextProps: Readonly

, + nextState: Readonly, + nextContext: any + ): void; + } + + interface Mixin extends ComponentLifecycle { + mixins?: Array>; + statics?: { + [key: string]: any; + }; + + displayName?: string; + propTypes?: ValidationMap; + contextTypes?: ValidationMap; + childContextTypes?: ValidationMap; + + getDefaultProps?(): P; + getInitialState?(): S; + } + + interface ComponentSpec extends Mixin { + render(): ReactNode; + + [propertyName: string]: any; + } + + function createRef(): RefObject; + + // will show `ForwardRef(${Component.displayName || Component.name})` in devtools by default, + // but can be given its own specific name + interface ForwardRefExoticComponent

extends NamedExoticComponent

{ + defaultProps?: Partial

; + propTypes?: WeakValidationMap

; + } + + function forwardRef( + Component: RefForwardingComponent + ): ForwardRefExoticComponent & RefAttributes>; + + /** Ensures that the props do not include ref at all */ + type PropsWithoutRef

= + // Just Pick would be sufficient for this, but I'm trying to avoid unnecessary mapping over union types + // https://github.com/Microsoft/TypeScript/issues/28339 + "ref" extends keyof P ? Pick> : P; + /** Ensures that the props do not include string ref, which cannot be forwarded */ + type PropsWithRef

= + // Just "P extends { ref?: infer R }" looks sufficient, but R will infer as {} if P is {}. + "ref" extends keyof P + ? P extends { ref?: infer R } + ? string extends R + ? PropsWithoutRef

& { ref?: Exclude } + : P + : P + : P; + + type PropsWithChildren

= P & { children?: ReactNode }; + + /** + * NOTE: prefer ComponentPropsWithRef, if the ref is forwarded, + * or ComponentPropsWithoutRef when refs are not supported. + */ + type ComponentProps< + T extends keyof JSX.IntrinsicElements | JSXElementConstructor + > = T extends JSXElementConstructor + ? P + : T extends keyof JSX.IntrinsicElements + ? JSX.IntrinsicElements[T] + : {}; + type ComponentPropsWithRef = T extends ComponentClass< + infer P + > + ? PropsWithoutRef

& RefAttributes> + : PropsWithRef>; + type ComponentPropsWithoutRef = PropsWithoutRef< + ComponentProps + >; + + // will show `Memo(${Component.displayName || Component.name})` in devtools by default, + // but can be given its own specific name + type MemoExoticComponent> = NamedExoticComponent< + ComponentPropsWithRef + > & { + readonly type: T; + }; + + function memo

( + Component: SFC

, + propsAreEqual?: ( + prevProps: Readonly>, + nextProps: Readonly> + ) => boolean + ): NamedExoticComponent

; + function memo>( + Component: T, + propsAreEqual?: ( + prevProps: Readonly>, + nextProps: Readonly> + ) => boolean + ): MemoExoticComponent; + + type LazyExoticComponent> = ExoticComponent< + ComponentPropsWithRef + > & { + readonly _result: T; + }; + + function lazy>( + factory: () => Promise<{ default: T }> + ): LazyExoticComponent; + + // + // React Hooks + // ---------------------------------------------------------------------- + + // based on the code in https://github.com/facebook/react/pull/13968 + + // Unlike the class component setState, the updates are not allowed to be partial + type SetStateAction = S | ((prevState: S) => S); + // this technically does accept a second argument, but it's already under a deprecation warning + // and it's not even released so probably better to not define it. + type Dispatch = (value: A) => void; + // Since action _can_ be undefined, dispatch may be called without any parameters. + type DispatchWithoutAction = () => void; + // Unlike redux, the actions _can_ be anything + type Reducer = (prevState: S, action: A) => S; + // If useReducer accepts a reducer without action, dispatch may be called without any parameters. + type ReducerWithoutAction = (prevState: S) => S; + // types used to try and prevent the compiler from reducing S + // to a supertype common with the second argument to useReducer() + type ReducerState> = R extends Reducer< + infer S, + any + > + ? S + : never; + type ReducerAction> = R extends Reducer< + any, + infer A + > + ? A + : never; + // The identity check is done with the SameValue algorithm (Object.is), which is stricter than === + type ReducerStateWithoutAction< + R extends ReducerWithoutAction + > = R extends ReducerWithoutAction ? S : never; + // TODO (TypeScript 3.0): ReadonlyArray + type DependencyList = ReadonlyArray; + + // NOTE: callbacks are _only_ allowed to return either void, or a destructor. + // The destructor is itself only allowed to return void. + type EffectCallback = () => void | (() => void | undefined); + + interface MutableRefObject { + current: T; + } + + // This will technically work if you give a Consumer or Provider but it's deprecated and warns + /** + * Accepts a context object (the value returned from `React.createContext`) and returns the current + * context value, as given by the nearest context provider for the given context. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usecontext + */ + function useContext( + context: Context /*, (not public API) observedBits?: number|boolean */ + ): T; + /** + * 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( + initialState: S | (() => S) + ): [S, Dispatch>]; + // 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, + Dispatch> + ]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + // overload where dispatch could accept 0 arguments. + function useReducer, I>( + reducer: R, + initializerArg: I, + initializer: (arg: I) => ReducerStateWithoutAction + ): [ReducerStateWithoutAction, DispatchWithoutAction]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + // overload where dispatch could accept 0 arguments. + function useReducer>( + reducer: R, + initializerArg: ReducerStateWithoutAction, + initializer?: undefined + ): [ReducerStateWithoutAction, DispatchWithoutAction]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + // overload where "I" may be a subset of ReducerState; used to provide autocompletion. + // If "I" matches ReducerState exactly then the last overload will allow initializer to be ommitted. + // the last overload effectively behaves as if the identity function (x => x) is the initializer. + function useReducer, I>( + reducer: R, + initializerArg: I & ReducerState, + initializer: (arg: I & ReducerState) => ReducerState + ): [ReducerState, Dispatch>]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + // overload for free "I"; all goes as long as initializer converts it into "ReducerState". + function useReducer, I>( + reducer: R, + initializerArg: I, + initializer: (arg: I) => ReducerState + ): [ReducerState, Dispatch>]; + /** + * An alternative to `useState`. + * + * `useReducer` is usually preferable to `useState` when you have complex state logic that involves + * multiple sub-values. It also lets you optimize performance for components that trigger deep + * updates because you can pass `dispatch` down instead of callbacks. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usereducer + */ + + // I'm not sure if I keep this 2-ary or if I make it (2,3)-ary; it's currently (2,3)-ary. + // The Flow types do have an overload for 3-ary invocation with undefined initializer. + + // NOTE: without the ReducerState indirection, TypeScript would reduce S to be the most common + // supertype between the reducer's return type and the initialState (or the initializer's return type), + // which would prevent autocompletion from ever working. + + // TODO: double-check if this weird overload logic is necessary. It is possible it's either a bug + // in older versions, or a regression in newer versions of the typescript completion service. + function useReducer>( + reducer: R, + initialState: ReducerState, + initializer?: undefined + ): [ReducerState, Dispatch>]; + /** + * `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. It’s handy for keeping any mutable + * value around similar to how you’d use instance fields in classes. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#useref + */ + // TODO (TypeScript 3.0): + function useRef(initialValue: T): MutableRefObject; + // convenience overload for refs given as a ref prop as they typically start with a null value + /** + * `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. It’s handy for keeping any mutable + * value around similar to how you’d use instance fields in classes. + * + * Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type + * of the generic argument. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#useref + */ + // TODO (TypeScript 3.0): + function useRef(initialValue: T | null): RefObject; + // 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. It’s handy for keeping any mutable + * value around similar to how you’d use instance fields in classes. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#useref + */ + // TODO (TypeScript 3.0): + function useRef(): MutableRefObject; + /** + * 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 + * `useLayoutEffect` will be flushed synchronously, before the browser has a chance to paint. + * + * Prefer the standard `useEffect` when possible to avoid blocking visual updates. + * + * If you’re migrating code from a class component, `useLayoutEffect` fires in the same phase as + * `componentDidMount` and `componentDidUpdate`. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#uselayouteffect + */ + function useLayoutEffect(effect: EffectCallback, deps?: DependencyList): void; + /** + * Accepts a function that contains imperative, possibly effectful code. + * + * @param effect Imperative function that can return a cleanup function + * @param deps If present, effect will only activate if the values in the list change. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#useeffect + */ + function useEffect(effect: EffectCallback, deps?: DependencyList): void; + // NOTE: this does not accept strings, but this will have to be fixed by removing strings from type Ref + /** + * `useImperativeHandle` customizes the instance value that is exposed to parent components when using + * `ref`. As always, imperative code using refs should be avoided in most cases. + * + * `useImperativeHandle` should be used with `React.forwardRef`. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#useimperativehandle + */ + function useImperativeHandle( + ref: Ref | undefined, + init: () => R, + deps?: DependencyList + ): void; + // I made 'inputs' required here and in useMemo as there's no point to memoizing without the memoization key + // useCallback(X) is identical to just using X, useMemo(() => Y) is identical to just using Y. + /** + * `useCallback` will return a memoized version of the callback that only changes if one of the `inputs` + * has changed. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usecallback + */ + // TODO (TypeScript 3.0): unknown> + function useCallback any>( + callback: T, + deps: DependencyList + ): T; + /** + * `useMemo` will only recompute the memoized value when one of the `deps` has changed. + * + * Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in + * the second argument. + * + * ```ts + * function expensive () { ... } + * + * function Component () { + * const expensiveResult = useMemo(expensive, [expensive]) + * return ... + * } + * ``` + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usememo + */ + // allow undefined, but don't make it optional as that is very likely a mistake + function useMemo(factory: () => T, deps: DependencyList | undefined): T; + /** + * `useDebugValue` can be used to display a label for custom hooks in React DevTools. + * + * NOTE: We don’t recommend adding debug values to every custom hook. + * It’s most valuable for custom hooks that are part of shared libraries. + * + * @version 16.8.0 + * @see https://reactjs.org/docs/hooks-reference.html#usedebugvalue + */ + // the name of the custom hook is itself derived from the function name at runtime: + // it's just the function name without the "use" prefix. + function useDebugValue(value: T, format?: (value: T) => any): void; + + // + // Event System + // ---------------------------------------------------------------------- + // TODO: change any to unknown when moving to TS v3 + interface BaseSyntheticEvent { + nativeEvent: E; + currentTarget: C; + target: T; + bubbles: boolean; + cancelable: boolean; + defaultPrevented: boolean; + eventPhase: number; + isTrusted: boolean; + preventDefault(): void; + isDefaultPrevented(): boolean; + stopPropagation(): void; + isPropagationStopped(): boolean; + persist(): void; + timeStamp: number; + type: string; + } + + /** + * currentTarget - a reference to the element on which the event listener is registered. + * + * target - a reference to the element from which the event was originally dispatched. + * This might be a child element to the element on which the event listener is registered. + * If you thought this should be `EventTarget & T`, see https://github.com/DefinitelyTyped/DefinitelyTyped/pull/12239 + */ + interface SyntheticEvent + extends BaseSyntheticEvent {} + + interface ClipboardEvent + extends SyntheticEvent { + clipboardData: DataTransfer; + } + + interface CompositionEvent + extends SyntheticEvent { + data: string; + } + + interface DragEvent extends MouseEvent { + dataTransfer: DataTransfer; + } + + interface PointerEvent + extends MouseEvent { + pointerId: number; + pressure: number; + tiltX: number; + tiltY: number; + width: number; + height: number; + pointerType: "mouse" | "pen" | "touch"; + isPrimary: boolean; + } + + interface FocusEvent + extends SyntheticEvent { + relatedTarget: EventTarget | null; + target: EventTarget & T; + } + + // tslint:disable-next-line:no-empty-interface + interface FormEvent extends SyntheticEvent {} + + interface InvalidEvent extends SyntheticEvent { + target: EventTarget & T; + } + + interface ChangeEvent extends SyntheticEvent { + target: EventTarget & T; + } + + interface KeyboardEvent + extends SyntheticEvent { + altKey: boolean; + charCode: number; + ctrlKey: boolean; + /** + * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. + */ + getModifierState(key: string): boolean; + /** + * See the [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#named-key-attribute-values). for possible values + */ + key: string; + keyCode: number; + locale: string; + location: number; + metaKey: boolean; + repeat: boolean; + shiftKey: boolean; + which: number; + } + + interface MouseEvent + extends SyntheticEvent { + altKey: boolean; + button: number; + buttons: number; + clientX: number; + clientY: number; + ctrlKey: boolean; + /** + * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. + */ + getModifierState(key: string): boolean; + metaKey: boolean; + movementX: number; + movementY: number; + pageX: number; + pageY: number; + relatedTarget: EventTarget | null; + screenX: number; + screenY: number; + shiftKey: boolean; + } + + interface TouchEvent + extends SyntheticEvent { + altKey: boolean; + changedTouches: TouchList; + ctrlKey: boolean; + /** + * See [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#keys-modifier). for a list of valid (case-sensitive) arguments to this method. + */ + getModifierState(key: string): boolean; + metaKey: boolean; + shiftKey: boolean; + targetTouches: TouchList; + touches: TouchList; + } + + interface UIEvent extends SyntheticEvent { + detail: number; + view: AbstractView; + } + + interface WheelEvent extends MouseEvent { + deltaMode: number; + deltaX: number; + deltaY: number; + deltaZ: number; + } + + interface AnimationEvent + extends SyntheticEvent { + animationName: string; + elapsedTime: number; + pseudoElement: string; + } + + interface TransitionEvent + extends SyntheticEvent { + elapsedTime: number; + propertyName: string; + pseudoElement: string; + } + + // + // Event Handler Types + // ---------------------------------------------------------------------- + + type EventHandler> = { + bivarianceHack(event: E): void; + }["bivarianceHack"]; + + type ReactEventHandler = EventHandler>; + + type ClipboardEventHandler = EventHandler>; + type CompositionEventHandler = EventHandler>; + type DragEventHandler = EventHandler>; + type FocusEventHandler = EventHandler>; + type FormEventHandler = EventHandler>; + type ChangeEventHandler = EventHandler>; + type KeyboardEventHandler = EventHandler>; + type MouseEventHandler = EventHandler>; + type TouchEventHandler = EventHandler>; + type PointerEventHandler = EventHandler>; + type UIEventHandler = EventHandler>; + type WheelEventHandler = EventHandler>; + type AnimationEventHandler = EventHandler>; + type TransitionEventHandler = EventHandler>; + + // + // Props / DOM Attributes + // ---------------------------------------------------------------------- + + /** + * @deprecated. This was used to allow clients to pass `ref` and `key` + * to `createElement`, which is no longer necessary due to intersection + * types. If you need to declare a props object before passing it to + * `createElement` or a factory, use `ClassAttributes`: + * + * ```ts + * var b: Button | null; + * var props: ButtonProps & ClassAttributes