diff --git a/types/meteor/mongo.d.ts b/types/meteor/mongo.d.ts index 82f7107d1b2b48..6b042fc77a1463 100644 --- a/types/meteor/mongo.d.ts +++ b/types/meteor/mongo.d.ts @@ -174,7 +174,7 @@ declare module 'meteor/mongo' { * Constructor for a Collection * @param name The name of the collection. If null, creates an unmanaged (unsynchronized) local collection. */ - new ( + new ( name: string | null, options?: { /** @@ -199,7 +199,7 @@ declare module 'meteor/mongo' { }, ): Collection; } - interface Collection { + interface Collection { allow = undefined>(options: { insert?: ((userId: string, doc: DispatchTransform) => boolean) | undefined; update?: diff --git a/types/orbit-ui__react-components/tsconfig.json b/types/orbit-ui__react-components/tsconfig.json index e6f7c99b217388..2177c56eeca69c 100644 --- a/types/orbit-ui__react-components/tsconfig.json +++ b/types/orbit-ui__react-components/tsconfig.json @@ -13,7 +13,8 @@ "forceConsistentCasingInFileNames": true, "jsx": "react", "paths": { - "@orbit-ui/*": ["orbit-ui__*"] + "@orbit-ui/*": ["orbit-ui__*"], + "react": ["react/v16"] } }, "files": ["index.d.ts", "orbit-ui__react-components-tests.tsx"] diff --git a/types/prop-types/index.d.ts b/types/prop-types/index.d.ts index ccce1ffe5a7281..46a58eb1fec7dd 100644 --- a/types/prop-types/index.d.ts +++ b/types/prop-types/index.d.ts @@ -17,10 +17,9 @@ export interface ReactElementLike { key: string | number | null; } -export interface ReactNodeArray extends Array {} +export interface ReactNodeArray extends Iterable {} export type ReactNodeLike = - | {} | ReactElementLike | ReactNodeArray | string diff --git a/types/react-dnd-multi-backend/react-dnd-multi-backend-tests.tsx b/types/react-dnd-multi-backend/react-dnd-multi-backend-tests.tsx index ec3b2eef31b07c..c602deaaae1c69 100644 --- a/types/react-dnd-multi-backend/react-dnd-multi-backend-tests.tsx +++ b/types/react-dnd-multi-backend/react-dnd-multi-backend-tests.tsx @@ -1,10 +1,13 @@ import * as React from 'react'; -import { DndProvider } from 'react-dnd'; +import { DndProviderProps } from 'react-dnd'; import MultiBackend, { createTransition, TouchTransition, Backends, Preview, PreviewGenerator } from 'react-dnd-multi-backend'; import HTML5ToTouchEsm from 'react-dnd-multi-backend/dist/esm/HTML5toTouch'; import HTML5ToTouchCjs from 'react-dnd-multi-backend/dist/cjs/HTML5toTouch'; import TouchBackend from 'react-dnd-touch-backend'; +// Legacy versions of `react-dnd` rely on implicit children of `React.FC` which was removed in `@types/react@18` +declare const DndProvider: React.FC>>; + const context = {}; /** diff --git a/types/react-dom/index.d.ts b/types/react-dom/index.d.ts index d9a3f7db9aba20..98f799af98434a 100644 --- a/types/react-dom/index.d.ts +++ b/types/react-dom/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for React (react-dom) 17.0 +// Type definitions for React (react-dom) 18.0 // Project: https://reactjs.org // Definitions by: Asana // AssureSign @@ -10,10 +10,6 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 -// NOTE: Users of the upcoming React 18 release should add a reference -// to 'react-dom/next' in their project. See next.d.ts's top comment -// for reference and documentation on how exactly to do it. - // 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. diff --git a/types/react-dom/test-utils/index.d.ts b/types/react-dom/test-utils/index.d.ts index 25bce6534a1a34..567dcf31c9ec3a 100644 --- a/types/react-dom/test-utils/index.d.ts +++ b/types/react-dom/test-utils/index.d.ts @@ -2,7 +2,7 @@ import { AbstractView, Component, ComponentClass, ReactElement, ReactInstance, ClassType, DOMElement, FunctionComponentElement, CElement, - ReactHTMLElement, DOMAttributes, SFC + ReactHTMLElement, DOMAttributes, FC } from 'react'; import * as ReactTestUtils from "."; @@ -194,7 +194,7 @@ export function isElementOfType

, T extends Element>( * Returns `true` if `element` is a React element whose type is of a React `componentClass`. */ export function isElementOfType

( - element: ReactElement, type: SFC

): element is FunctionComponentElement

; + element: ReactElement, type: FC

): element is FunctionComponentElement

; /** * Returns `true` if `element` is a React element whose type is of a React `componentClass`. */ diff --git a/types/react-dom/test/next-tests.tsx b/types/react-dom/test/next-tests.tsx index e94ad090fa74d5..ce82938506e415 100644 --- a/types/react-dom/test/next-tests.tsx +++ b/types/react-dom/test/next-tests.tsx @@ -1,28 +1 @@ /// -import React = require('react'); -import ReactDOMClient = require('react-dom/client'); - -function createRoot() { - const root = ReactDOMClient.createRoot(document.documentElement); - - root.render(

initial render
); - - // only makes sense for `hydrateRoot` - // $ExpectError - ReactDOMClient.createRoot(document); -} - -function hydrateRoot() { - const hydrateable = ReactDOMClient.hydrateRoot(document,
initial render
, { - identifierPrefix: 'react-18-app', - onRecoverableError: error => { - console.error(error); - }, - }); - hydrateable.render(
render update
); - ReactDOMClient.hydrateRoot(document, { - // Forgot `initialChildren` - // $ExpectError - identifierPrefix: 'react-18-app', - }); -} diff --git a/types/react-dom/test/react-dom-tests.tsx b/types/react-dom/test/react-dom-tests.tsx index 2ec52495aa930a..99b454c9457f38 100644 --- a/types/react-dom/test/react-dom-tests.tsx +++ b/types/react-dom/test/react-dom-tests.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import * as ReactDOM from 'react-dom'; +import * as ReactDOMClient from 'react-dom/client'; import * as ReactDOMServer from 'react-dom/server'; -import * as ReactDOMNodeStream from 'react-dom/node-stream'; import * as ReactTestUtils from 'react-dom/test-utils'; declare function describe(desc: string, f: () => void): void; @@ -91,16 +91,6 @@ describe('ReactDOMServer', () => { }); }); -describe('ReactDOMNodeStream', () => { - it('renderToStream', () => { - const content: any = ReactDOMNodeStream.renderToStream(React.createElement('div')); - }); - - it('renderToStaticStream', () => { - const content: any = ReactDOMNodeStream.renderToStaticStream(React.createElement('div')); - }); -}); - describe('React dom test utils', () => { it('Simulate', () => { const element = document.createElement('div'); @@ -237,3 +227,28 @@ describe('React dom test utils', () => { }); }); }); + +function createRoot() { + const root = ReactDOMClient.createRoot(document.documentElement); + + root.render(
initial render
); + + // only makes sense for `hydrateRoot` + // $ExpectError + ReactDOMClient.createRoot(document); +} + +function hydrateRoot() { + const hydrateable = ReactDOMClient.hydrateRoot(document,
initial render
, { + identifierPrefix: 'react-18-app', + onRecoverableError: error => { + console.error(error); + }, + }); + hydrateable.render(
render update
); + ReactDOMClient.hydrateRoot(document, { + // Forgot `initialChildren` + // $ExpectError + identifierPrefix: 'react-18-app', + }); +} diff --git a/types/react-dom/v17/index.d.ts b/types/react-dom/v17/index.d.ts new file mode 100644 index 00000000000000..83e2570656568d --- /dev/null +++ b/types/react-dom/v17/index.d.ts @@ -0,0 +1,100 @@ +// Type definitions for React (react-dom) 17.0 +// Project: https://reactjs.org +// Definitions by: Asana +// AssureSign +// Microsoft +// MartynasZilinskas +// Josh Rutherford +// Jessica Franco +// Sebastian Silbermann +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.8 + +export as namespace ReactDOM; + +import { + ReactInstance, Component, ComponentState, + ReactElement, FunctionComponentElement, CElement, + DOMAttributes, DOMElement, ReactNode, ReactPortal +} from 'react'; + +export function findDOMNode(instance: ReactInstance | null | undefined): Element | null | Text; +export function unmountComponentAtNode(container: Element | DocumentFragment): 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 flushSync(fn: () => R): R; +export function flushSync(fn: (a: A) => R, a: A): R; + +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>( + 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 type Container = Element | Document | DocumentFragment; + +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: Container| null, + callback?: () => void + ): T; + + ( + element: Array, any>>, + container: Container| null, + callback?: () => void + ): Element; + + ( + element: FunctionComponentElement | Array>, + container: Container| null, + callback?: () => void + ): void; + + >( + element: CElement, + container: Container| null, + callback?: () => void + ): T; + + ( + element: Array>>, + container: Container| null, + callback?: () => void + ): Component; + +

( + element: ReactElement

, + container: Container| null, + callback?: () => void + ): Component | Element | void; + + ( + element: ReactElement[], + container: Container| null, + callback?: () => void + ): Component | Element | void; +} diff --git a/types/react-dom/node-stream/index.d.ts b/types/react-dom/v17/node-stream/index.d.ts similarity index 100% rename from types/react-dom/node-stream/index.d.ts rename to types/react-dom/v17/node-stream/index.d.ts diff --git a/types/react-dom/v17/server.d.ts b/types/react-dom/v17/server.d.ts new file mode 100644 index 00000000000000..97747165a8f381 --- /dev/null +++ b/types/react-dom/v17/server.d.ts @@ -0,0 +1,47 @@ +// forward declarations +declare global { + namespace NodeJS { + // tslint:disable-next-line:no-empty-interface + interface ReadableStream {} + } +} + +import { ReactElement } from 'react'; + +/** + * Render a React element to its initial HTML. This should only be used on the server. + * React will return an HTML string. You can use this method to generate HTML on the server + * and send the markup down on the initial request for faster page loads and to allow search + * engines to crawl your pages for SEO purposes. + * + * If you call `ReactDOM.hydrate()` on a node that already has this server-rendered markup, + * React will preserve it and only attach event handlers, allowing you + * to have a very performant first-load experience. + */ +export function renderToString(element: ReactElement): string; + +/** + * Render a React element to its initial HTML. Returns a Readable stream that outputs + * an HTML string. The HTML output by this stream is exactly equal to what + * `ReactDOMServer.renderToString()` would return. + */ +export function renderToNodeStream(element: ReactElement): NodeJS.ReadableStream; + +/** + * Similar to `renderToString`, except this doesn't create extra DOM attributes + * such as `data-reactid`, that React uses internally. This is useful if you want + * to use React as a simple static page generator, as stripping away the extra + * attributes can save lots of bytes. + */ +export function renderToStaticMarkup(element: ReactElement): string; + +/** + * Similar to `renderToNodeStream`, except this doesn't create extra DOM attributes + * such as `data-reactid`, that React uses internally. The HTML output by this stream + * is exactly equal to what `ReactDOMServer.renderToStaticMarkup()` would return. + */ +export function renderToStaticNodeStream(element: ReactElement): NodeJS.ReadableStream; + +export const version: string; + +export as namespace ReactDOMServer; diff --git a/types/react-dom/v17/test-utils/index.d.ts b/types/react-dom/v17/test-utils/index.d.ts new file mode 100644 index 00000000000000..567dcf31c9ec3a --- /dev/null +++ b/types/react-dom/v17/test-utils/index.d.ts @@ -0,0 +1,307 @@ +import { + AbstractView, Component, ComponentClass, + ReactElement, ReactInstance, ClassType, + DOMElement, FunctionComponentElement, CElement, + ReactHTMLElement, DOMAttributes, FC +} from 'react'; + +import * as ReactTestUtils from "."; + +export {}; + +export interface OptionalEventProperties { + bubbles?: boolean | undefined; + cancelable?: boolean | undefined; + currentTarget?: EventTarget | undefined; + defaultPrevented?: boolean | undefined; + eventPhase?: number | undefined; + isTrusted?: boolean | undefined; + nativeEvent?: Event | undefined; + preventDefault?(): void; + stopPropagation?(): void; + target?: EventTarget | undefined; + timeStamp?: Date | undefined; + type?: string | undefined; +} + +export interface SyntheticEventData extends OptionalEventProperties { + altKey?: boolean | undefined; + button?: number | undefined; + buttons?: number | undefined; + clientX?: number | undefined; + clientY?: number | undefined; + changedTouches?: TouchList | undefined; + charCode?: number | undefined; + clipboardData?: DataTransfer | undefined; + ctrlKey?: boolean | undefined; + deltaMode?: number | undefined; + deltaX?: number | undefined; + deltaY?: number | undefined; + deltaZ?: number | undefined; + detail?: number | undefined; + getModifierState?(key: string): boolean; + key?: string | undefined; + keyCode?: number | undefined; + locale?: string | undefined; + location?: number | undefined; + metaKey?: boolean | undefined; + pageX?: number | undefined; + pageY?: number | undefined; + relatedTarget?: EventTarget | undefined; + repeat?: boolean | undefined; + screenX?: number | undefined; + screenY?: number | undefined; + shiftKey?: boolean | undefined; + targetTouches?: TouchList | undefined; + touches?: TouchList | undefined; + view?: AbstractView | undefined; + which?: number | undefined; +} + +export type EventSimulator = (element: Element | Component, eventData?: SyntheticEventData) => void; + +export interface MockedComponentClass { + new (props: any): any; +} + +export interface ShallowRenderer { + /** + * After `shallowRenderer.render()` has been called, returns shallowly rendered output. + */ + getRenderOutput(): E; + /** + * Similar to `ReactDOM.render` but it doesn't require DOM and only renders a single level deep. + */ + render(element: ReactElement, context?: any): void; + unmount(): void; +} + +/** + * Simulate an event dispatch on a DOM node with optional `eventData` event data. + * `Simulate` has a method for every event that React understands. + */ +export namespace Simulate { + const abort: EventSimulator; + const animationEnd: EventSimulator; + const animationIteration: EventSimulator; + const animationStart: EventSimulator; + const blur: EventSimulator; + const canPlay: EventSimulator; + const canPlayThrough: EventSimulator; + const change: EventSimulator; + const click: EventSimulator; + const compositionEnd: EventSimulator; + const compositionStart: EventSimulator; + const compositionUpdate: EventSimulator; + const contextMenu: EventSimulator; + const copy: EventSimulator; + const cut: EventSimulator; + const doubleClick: EventSimulator; + const drag: EventSimulator; + const dragEnd: EventSimulator; + const dragEnter: EventSimulator; + const dragExit: EventSimulator; + const dragLeave: EventSimulator; + const dragOver: EventSimulator; + const dragStart: EventSimulator; + const drop: EventSimulator; + const durationChange: EventSimulator; + const emptied: EventSimulator; + const encrypted: EventSimulator; + const ended: EventSimulator; + const error: EventSimulator; + const focus: EventSimulator; + const input: EventSimulator; + const invalid: EventSimulator; + const keyDown: EventSimulator; + const keyPress: EventSimulator; + const keyUp: EventSimulator; + const load: EventSimulator; + const loadStart: EventSimulator; + const loadedData: EventSimulator; + const loadedMetadata: EventSimulator; + const mouseDown: EventSimulator; + const mouseEnter: EventSimulator; + const mouseLeave: EventSimulator; + const mouseMove: EventSimulator; + const mouseOut: EventSimulator; + const mouseOver: EventSimulator; + const mouseUp: EventSimulator; + const paste: EventSimulator; + const pause: EventSimulator; + const play: EventSimulator; + const playing: EventSimulator; + const progress: EventSimulator; + const rateChange: EventSimulator; + const scroll: EventSimulator; + const seeked: EventSimulator; + const seeking: EventSimulator; + const select: EventSimulator; + const stalled: EventSimulator; + const submit: EventSimulator; + const suspend: EventSimulator; + const timeUpdate: EventSimulator; + const touchCancel: EventSimulator; + const touchEnd: EventSimulator; + const touchMove: EventSimulator; + const touchStart: EventSimulator; + const transitionEnd: EventSimulator; + const volumeChange: EventSimulator; + const waiting: EventSimulator; + const wheel: EventSimulator; +} + +/** + * Render a React element into a detached DOM node in the document. __This function requires a DOM__. + */ +export function renderIntoDocument( + element: DOMElement): T; +export function renderIntoDocument( + element: FunctionComponentElement): void; +// If we replace `P` with `any` in this overload, then some tests fail because +// calls to `renderIntoDocument` choose the last overload on the +// subtype-relation pass and get an undesirably broad return type. Using `P` +// allows this overload to match on the subtype-relation pass. +export function renderIntoDocument>( + element: CElement): T; +export function renderIntoDocument

( + element: ReactElement

): Component

| Element | void; + +/** + * Pass a mocked component module to this method to augment it with useful methods that allow it to + * be used as a dummy React component. Instead of rendering as usual, the component will become + * a simple `

` (or other tag if `mockTagName` is provided) containing any provided children. + */ +export function mockComponent( + mocked: MockedComponentClass, mockTagName?: string): typeof ReactTestUtils; + +/** + * Returns `true` if `element` is any React element. + */ +export function isElement(element: any): boolean; + +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType( + element: ReactElement, type: string): element is ReactHTMLElement; +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType

, T extends Element>( + element: ReactElement, type: string): element is DOMElement; +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType

( + element: ReactElement, type: FC

): element is FunctionComponentElement

; +/** + * Returns `true` if `element` is a React element whose type is of a React `componentClass`. + */ +export function isElementOfType, C extends ComponentClass

>( + element: ReactElement, type: ClassType): element is CElement; + +/** + * Returns `true` if `instance` is a DOM component (such as a `

` or ``). + */ +export function isDOMComponent(instance: ReactInstance): instance is Element; +/** + * Returns `true` if `instance` is a user-defined component, such as a class or a function. + */ +export function isCompositeComponent(instance: ReactInstance): instance is Component; +/** + * Returns `true` if `instance` is a component whose type is of a React `componentClass`. + */ +export function isCompositeComponentWithType, C extends ComponentClass>( + instance: ReactInstance, type: ClassType): boolean; + +/** + * Traverse all components in `tree` and accumulate all components where + * `test(component)` is `true`. This is not that useful on its own, but it's used + * as a primitive for other test utils. + */ +export function findAllInRenderedTree( + root: Component, + fn: (i: ReactInstance) => boolean): ReactInstance[]; + +/** + * Finds all DOM elements of components in the rendered tree that are + * DOM components with the class name matching `className`. + */ +export function scryRenderedDOMComponentsWithClass( + root: Component, + className: string): Element[]; +/** + * Like `scryRenderedDOMComponentsWithClass()` but expects there to be one result, + * and returns that one result, or throws exception if there is any other + * number of matches besides one. + */ +export function findRenderedDOMComponentWithClass( + root: Component, + className: string): Element; + +/** + * Finds all DOM elements of components in the rendered tree that are + * DOM components with the tag name matching `tagName`. + */ +export function scryRenderedDOMComponentsWithTag( + root: Component, + tagName: string): Element[]; +/** + * Like `scryRenderedDOMComponentsWithTag()` but expects there to be one result, + * and returns that one result, or throws exception if there is any other + * number of matches besides one. + */ +export function findRenderedDOMComponentWithTag( + root: Component, + tagName: string): Element; + +/** + * Finds all instances of components with type equal to `componentClass`. + */ +export function scryRenderedComponentsWithType, C extends ComponentClass>( + root: Component, + type: ClassType): T[]; + +/** + * Same as `scryRenderedComponentsWithType()` but expects there to be one result + * and returns that one result, or throws exception if there is any other + * number of matches besides one. + */ +export function findRenderedComponentWithType, C extends ComponentClass>( + root: Component, + type: ClassType): T; + +/** + * Call this in your tests to create a shallow renderer. + */ +export function createRenderer(): ShallowRenderer; + +/** + * Wrap any code rendering and triggering updates to your components into `act()` calls. + * + * Ensures that the behavior in your tests matches what happens in the browser + * more closely by executing pending `useEffect`s before returning. This also + * reduces the amount of re-renders done. + * + * @param callback A synchronous, void callback that will execute as a single, complete React commit. + * + * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks + */ +// NOTES +// - the order of these signatures matters - typescript will check the signatures in source order. +// If the `() => VoidOrUndefinedOnly` signature is first, it'll erroneously match a Promise returning function for users with +// `strictNullChecks: false`. +// - VoidOrUndefinedOnly is there to forbid any non-void return values for users with `strictNullChecks: true` +declare const UNDEFINED_VOID_ONLY: unique symbol; +// tslint:disable-next-line: void-return +type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never }; +export function act(callback: () => Promise): Promise; +export function act(callback: () => VoidOrUndefinedOnly): void; + +// Intentionally doesn't extend PromiseLike. +// Ideally this should be as hard to accidentally use as possible. +export interface DebugPromiseLike { + // the actual then() in here is 0-ary, but that doesn't count as a PromiseLike. + then(onfulfilled: (value: never) => never, onrejected: (reason: never) => never): never; +} diff --git a/types/react-dom/v17/test/react-dom-tests.tsx b/types/react-dom/v17/test/react-dom-tests.tsx new file mode 100644 index 00000000000000..2ec52495aa930a --- /dev/null +++ b/types/react-dom/v17/test/react-dom-tests.tsx @@ -0,0 +1,239 @@ +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import * as ReactDOMServer from 'react-dom/server'; +import * as ReactDOMNodeStream from 'react-dom/node-stream'; +import * as ReactTestUtils from 'react-dom/test-utils'; + +declare function describe(desc: string, f: () => void): void; +declare function it(desc: string, f: () => void): void; + +class TestComponent extends React.Component<{x: string}> { } + +describe('ReactDOM', () => { + it('render', () => { + const rootElement = document.createElement('div'); + ReactDOM.render(React.createElement('div'), rootElement); + ReactDOM.render(React.createElement('div'), document.createDocumentFragment()); + ReactDOM.render(React.createElement('div'), document); + }); + + it('hydrate', () => { + const rootElement = document.createElement('div'); + ReactDOM.hydrate(React.createElement('div'), rootElement); + ReactDOM.hydrate(React.createElement('div'), document.createDocumentFragment()); + ReactDOM.hydrate(React.createElement('div'), document); + }); + + it('unmounts', () => { + const rootElement = document.createElement('div'); + ReactDOM.render(React.createElement('div'), rootElement); + ReactDOM.unmountComponentAtNode(rootElement); + }); + + it('works with document fragments', () => { + const fragment = document.createDocumentFragment(); + ReactDOM.render(React.createElement('div'), fragment); + ReactDOM.unmountComponentAtNode(fragment); + }); + + it('find dom node', () => { + const rootElement = document.createElement('div'); + ReactDOM.render(React.createElement('div'), rootElement); + ReactDOM.findDOMNode(rootElement); + ReactDOM.findDOMNode(null); + ReactDOM.findDOMNode(undefined); + }); + + it('createPortal', () => { + const rootElement = document.createElement('div'); + const portalTarget = document.createElement('div'); + + class ClassComponent extends React.Component { + render() { + return ReactDOM.createPortal(
, portalTarget); + } + } + + ReactDOM.createPortal(
, document.createElement('div')); + ReactDOM.createPortal(
, document.createElement('div'), null); + ReactDOM.createPortal(
, document.createElement('div'), 'key'); + + ReactDOM.createPortal(React.createElement('div'), document.createElement('div')); + ReactDOM.createPortal(React.createElement('div'), document.createElement('div'), null); + ReactDOM.createPortal(React.createElement('div'), document.createElement('div'), 'key'); + + ReactDOM.render(, rootElement); + }); + + it('flushSync', () => { + // $ExpectType void + ReactDOM.flushSync(() => {}); + // $ExpectType number + ReactDOM.flushSync(() => 42); + // $ExpectType number + ReactDOM.flushSync(() => 42, 'not used'); + // $ExpectType number + ReactDOM.flushSync((a: string) => 42, 'not used'); + // $ExpectError + ReactDOM.flushSync((a: string) => 42); + // $ExpectError + ReactDOM.flushSync((a: string) => 42, 100); + }); +}); + +describe('ReactDOMServer', () => { + it('renderToString', () => { + const content: string = ReactDOMServer.renderToString(React.createElement('div')); + }); + + it('renderToStaticMarkup', () => { + const content: string = ReactDOMServer.renderToStaticMarkup(React.createElement('div')); + }); +}); + +describe('ReactDOMNodeStream', () => { + it('renderToStream', () => { + const content: any = ReactDOMNodeStream.renderToStream(React.createElement('div')); + }); + + it('renderToStaticStream', () => { + const content: any = ReactDOMNodeStream.renderToStaticStream(React.createElement('div')); + }); +}); + +describe('React dom test utils', () => { + it('Simulate', () => { + const element = document.createElement('div'); + const dom = ReactDOM.render( + React.createElement('input', { type: 'text' }), + element + ) as Element; + const node = ReactDOM.findDOMNode(dom) as HTMLInputElement; + + node.value = 'giraffe'; + ReactTestUtils.Simulate.change(node); + ReactTestUtils.Simulate.keyDown(node, { key: "Enter", keyCode: 13, which: 13 }); + }); + + it('renderIntoDocument', () => { + const element = React.createElement('input', { type: 'text' }); + ReactTestUtils.renderIntoDocument(element); + }); + + it('mockComponent', () => { + ReactTestUtils.mockComponent(TestComponent, 'div'); + }); + + it('isElement', () => { + const element = React.createElement(TestComponent); + const isReactElement: boolean = ReactTestUtils.isElement(element); + }); + + it('isElementOfType', () => { + const element = React.createElement(TestComponent); + const isReactElement: boolean = ReactTestUtils.isElementOfType(element, TestComponent); + }); + + it('isDOMComponent', () => { + const element = React.createElement('div'); + const instance = ReactTestUtils.renderIntoDocument(element) as HTMLDivElement; + const isDOMElement: boolean = ReactTestUtils.isDOMComponent(instance); + }); + + it('isCompositeComponent', () => { + const element = React.createElement(TestComponent); + const instance: TestComponent = ReactTestUtils.renderIntoDocument(element); + const isCompositeComponent: boolean = ReactTestUtils.isCompositeComponent(instance); + }); + + it('isCompositeComponentWithType', () => { + const element = React.createElement(TestComponent); + const instance: TestComponent = ReactTestUtils.renderIntoDocument(element); + const isCompositeComponent: boolean = ReactTestUtils.isCompositeComponentWithType(instance, TestComponent); + }); + + it('findAllInRenderedTree', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findAllInRenderedTree(component, (i: React.ReactInstance) => true); + }); + + it('scryRenderedDOMComponentsWithClass', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.scryRenderedDOMComponentsWithClass(component, 'class'); + }); + + it('findRenderedDOMComponentWithClass', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findRenderedDOMComponentWithClass(component, 'class'); + }); + + it('scryRenderedDOMComponentsWithTag', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.scryRenderedDOMComponentsWithTag(component, 'div'); + }); + + it('findRenderedDOMComponentWithTag', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findRenderedDOMComponentWithTag(component, 'tag'); + }); + + it('scryRenderedComponentsWithType', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.scryRenderedComponentsWithType(component, TestComponent); + }); + + it('findRenderedComponentWithType', () => { + const component = ReactTestUtils.renderIntoDocument(React.createElement(TestComponent)); + ReactTestUtils.findRenderedComponentWithType(component, TestComponent); + }); + + describe('Shallow Rendering', () => { + it('createRenderer', () => { + const component = React.createElement(TestComponent); + const shallowRenderer = ReactTestUtils.createRenderer(); + }); + + it('shallowRenderer.render', () => { + const component = React.createElement(TestComponent); + const shallowRenderer = ReactTestUtils.createRenderer(); + shallowRenderer.render(component); + }); + + it('shallowRenderer.getRenderOutput', () => { + const component = React.createElement(TestComponent); + const shallowRenderer = ReactTestUtils.createRenderer(); + shallowRenderer.getRenderOutput(); + }); + }); + + describe('act', () => { + describe('with sync callback', () => { + it('accepts a callback that is void', () => { + ReactTestUtils.act(() => {}); + }); + it('rejects a callback that returns null', () => { + // $ExpectError + ReactTestUtils.act(() => null); + }); + it('returns a type that is not Promise-like', () => { + // tslint:disable-next-line no-void-expression + const result = ReactTestUtils.act(() => {}); + // $ExpectError + result.then((x) => {}); + }); + }); + describe('with async callback', () => { + it('accepts a callback that is void', async () => { + await ReactTestUtils.act(async () => {}); + }); + it('rejects a callback that returns a value', async () => { + // $ExpectError + await ReactTestUtils.act(async () => null); + }); + it('returns a Promise-like', () => { + const result = ReactTestUtils.act(async () => {}); + result.then((x) => {}); + }); + }); + }); +}); diff --git a/types/react-dom/v17/tsconfig.json b/types/react-dom/v17/tsconfig.json new file mode 100644 index 00000000000000..dbd185b2e679b1 --- /dev/null +++ b/types/react-dom/v17/tsconfig.json @@ -0,0 +1,36 @@ +{ + "files": [ + "index.d.ts", + "test/react-dom-tests.tsx" + ], + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6", + "dom" + ], + "paths": { + "react": [ + "react/v17" + ], + "react-dom": [ + "react-dom/v17" + ], + "react-dom/*": [ + "react-dom/v17/*" + ] + }, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "jsx": "preserve" + } +} diff --git a/types/react-dom/v17/tslint.json b/types/react-dom/v17/tslint.json new file mode 100644 index 00000000000000..df0e470257e793 --- /dev/null +++ b/types/react-dom/v17/tslint.json @@ -0,0 +1,7 @@ +{ + "extends": "@definitelytyped/dtslint/dt.json", + "rules": { + "dt-header": false, + "no-unnecessary-generics": false + } +} diff --git a/types/react-native/v0.65/index.d.ts b/types/react-native/v0.65/index.d.ts index 104fe5d22f25ae..7d184c2dcd339a 100644 --- a/types/react-native/v0.65/index.d.ts +++ b/types/react-native/v0.65/index.d.ts @@ -360,7 +360,7 @@ export interface HostComponent

extends Pick, Exclude< // see react-jsx.d.ts export function createElement

( - type: React.ReactType, + type: React.ElementType, props?: P, ...children: React.ReactNode[] ): React.ReactElement

; diff --git a/types/react-relay/test/react-relay-tests.tsx b/types/react-relay/test/react-relay-tests.tsx index 41a752db2e4321..961e10e906873b 100644 --- a/types/react-relay/test/react-relay-tests.tsx +++ b/types/react-relay/test/react-relay-tests.tsx @@ -717,7 +717,7 @@ requestSubscription( ReactRelayContext.Consumer.prototype; ReactRelayContext.Provider.prototype; -const MyRelayContextProvider: React.FunctionComponent = ({children}) => { +const MyRelayContextProvider: React.FunctionComponent<{ children?: React.ReactNode }> = ({children}) => { return ( ; + + /** + * this should be an internal type + */ + interface MutableSource { + _source: T; + } + + export type MutableSourceSubscribe = (source: T, callback: () => void) => () => void; + + // TODO: This may not be intentionally part of the experimental release considering useMutableSource is no longer available + /** + * @param source A source could be anything as long as they can be subscribed to and have a "version". + * @param getVersion A function returns a value which will change whenever part of the source changes. + */ + export function unstable_createMutableSource(source: T, getVersion: () => any): MutableSource; } diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 958002af94c677..b668622a2c62b8 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for React 17.0 +// Type definitions for React 18.0 // Project: http://facebook.github.io/react/ // Definitions by: Asana // AssureSign @@ -29,10 +29,6 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.8 -// NOTE: Users of the upcoming React 18 release should add a reference -// to 'react/next' in their project. See next.d.ts's top comment -// for reference and documentation on how exactly to do it. - // NOTE: Users of the `experimental` builds of React should add a reference // to 'react/experimental' in their project. See experimental.d.ts's top comment // for reference and documentation on how exactly to do it. @@ -60,6 +56,7 @@ type Booleanish = boolean | 'true' | 'false'; declare const UNDEFINED_VOID_ONLY: unique symbol; // Destructors are only allowed to return void. type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never }; +type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never }; // tslint:disable-next-line:export-just-namespace export = React; @@ -75,10 +72,6 @@ declare namespace React { [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

= @@ -155,11 +148,6 @@ declare namespace React { 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 | undefined } ? R : never : never) | undefined; } @@ -237,7 +225,7 @@ declare namespace React { * @deprecated Use either `ReactNode[]` if you need an array or `Iterable` if its passed to a host component. */ interface ReactNodeArray extends ReadonlyArray {} - type ReactFragment = {} | Iterable; + type ReactFragment = Iterable; type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined; // @@ -396,16 +384,10 @@ declare namespace React { interface SuspenseProps { children?: ReactNode | undefined; - // TODO(react18): `fallback?: ReactNode;` /** A fallback react tree to show when a Suspense child (like React.lazy) suspends */ - fallback: NonNullable|null; + fallback?: ReactNode; } - // TODO(react18): Updated JSDoc to reflect that Suspense works on the server. - /** - * 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; @@ -476,8 +458,7 @@ declare namespace React { * * @see https://reactjs.org/docs/context.html */ - // TODO (TypeScript 3.0): unknown - context: any; + context: unknown; constructor(props: Readonly

| P); /** @@ -497,12 +478,7 @@ declare namespace React { 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 | undefined }>; + readonly props: Readonly

; state: Readonly; /** * @deprecated @@ -529,26 +505,10 @@ declare namespace React { // 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; + (props: P, context?: any): ReactElement | null; propTypes?: WeakValidationMap

| undefined; contextTypes?: ValidationMap | undefined; defaultProps?: Partial

| undefined; @@ -568,7 +528,7 @@ declare namespace React { type ForwardedRef = ((instance: T | null) => void) | MutableRefObject | null; interface ForwardRefRenderFunction { - (props: PropsWithChildren

, ref: ForwardedRef): ReactElement | null; + (props: P, ref: ForwardedRef): ReactElement | null; displayName?: string | undefined; // explicit rejected with `never` required due to // https://github.com/microsoft/TypeScript/issues/36826 @@ -582,12 +542,6 @@ declare namespace React { propTypes?: never | undefined; } - /** - * @deprecated Use ForwardRefRenderFunction. forwardRef doesn't accept a - * "real" component. - */ - interface RefForwardingComponent extends ForwardRefRenderFunction {} - interface ComponentClass

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

| undefined; @@ -862,7 +816,7 @@ declare namespace React { function memo

( Component: FunctionComponent

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

, nextProps: Readonly

) => boolean ): NamedExoticComponent

; function memo>( Component: T, @@ -901,8 +855,7 @@ declare namespace React { // The identity check is done with the SameValue algorithm (Object.is), which is stricter than === type ReducerStateWithoutAction> = R extends ReducerWithoutAction ? S : never; - // TODO (TypeScript 3.0): ReadonlyArray - type DependencyList = ReadonlyArray; + type DependencyList = ReadonlyArray; // NOTE: callbacks are _only_ allowed to return either void, or a destructor. type EffectCallback = () => (void | Destructor); @@ -1109,8 +1062,10 @@ declare namespace React { * @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; + // A specific function type would not trigger implicit any. + // See https://github.com/DefinitelyTyped/DefinitelyTyped/issues/52873#issuecomment-845806435 for a comparison between `Function` and more specific types. + // tslint:disable-next-line ban-types + function useCallback(callback: T, deps: DependencyList): T; /** * `useMemo` will only recompute the memoized value when one of the `deps` has changed. * @@ -1132,6 +1087,83 @@ declare namespace React { // it's just the function name without the "use" prefix. function useDebugValue(value: T, format?: (value: T) => any): void; + // must be synchronous + export type TransitionFunction = () => VoidOrUndefinedOnly; + // strange definition to allow vscode to show documentation on the invocation + export interface TransitionStartFunction { + /** + * State updates caused inside the callback are allowed to be deferred. + * + * **If some state update causes a component to suspend, that state update should be wrapped in a transition.** + * + * @param callback A _synchronous_ function which causes state updates that can be deferred. + */ + (callback: TransitionFunction): void; + } + + /** + * Returns a deferred version of the value that may “lag behind” it for at most `timeoutMs`. + * + * This is commonly used to keep the interface responsive when you have something that renders immediately + * based on user input and something that needs to wait for a data fetch. + * + * A good example of this is a text input. + * + * @param value The value that is going to be deferred + * + * @see https://reactjs.org/docs/concurrent-mode-reference.html#usedeferredvalue + */ + export function useDeferredValue(value: T): T; + + /** + * Allows components to avoid undesirable loading states by waiting for content to load + * before transitioning to the next screen. It also allows components to defer slower, + * data fetching updates until subsequent renders so that more crucial updates can be + * rendered immediately. + * + * The `useTransition` hook returns two values in an array. + * + * The first is a boolean, React’s way of informing us whether we’re waiting for the transition to finish. + * The second is a function that takes a callback. We can use it to tell React which state we want to defer. + * + * **If some state update causes a component to suspend, that state update should be wrapped in a transition.** + * + * @param config An optional object with `timeoutMs` + * + * @see https://reactjs.org/docs/concurrent-mode-reference.html#usetransition + */ + export function useTransition(): [boolean, TransitionStartFunction]; + + /** + * Similar to `useTransition` but allows uses where hooks are not available. + * + * @param callback A _synchronous_ function which causes state updates that can be deferred. + */ + export function startTransition(scope: TransitionFunction): void; + + export function useId(): string; + + /** + * @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. + * + * @see https://github.com/facebook/react/pull/21913 + */ + export function useInsertionEffect(effect: EffectCallback, deps?: DependencyList): void; + + /** + * @param subscribe + * @param getSnapshot + * + * @see https://github.com/reactwg/react-18/discussions/86 + */ + // keep in sync with `useSyncExternalStore` from `use-sync-external-store` + export function useSyncExternalStore( + subscribe: (onStoreChange: () => void) => () => void, + getSnapshot: () => Snapshot, + getServerSnapshot?: () => Snapshot, + ): Snapshot; + // // Event System // ---------------------------------------------------------------------- @@ -1316,26 +1348,6 @@ declare namespace React { // 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; +}); + +interface AppState { + name: string; + age: number; +} + +type AppActions = + | { type: "getOlder" } + | { type: "resetAge" }; + +function reducer(s: AppState, action: AppActions): AppState { + switch (action.type) { + case "getOlder": + return { ...s, age: s.age + 1 }; + case "resetAge": + return { ...s, age: 0 }; + } +} + +const initialState = { + name: "Daniel", + age: 26 +}; + +export function App() { + const [state, dispatch] = React.useReducer(reducer, initialState); + const birthdayRef = React.useRef>(null); + + React.useLayoutEffect(() => { + if (birthdayRef.current !== null) { + birthdayRef.current.fancyClick(); + } else { + // this looks redundant but it ensures the type actually has "null" in it instead of "never" + // $ExpectType null + birthdayRef.current; + } + }); + + return <> + + dispatch({ type: "getOlder" })}> + Birthday time! + + dispatch({ type: "resetAge" })}> + Let's start over. + + ; +} + +interface Context { + test: true; +} +const context = React.createContext({ test: true }); + +function useEveryHook(ref: React.Ref<{ id: number }>|undefined): () => boolean { + const value: Context = React.useContext(context); + const [, setState] = React.useState(() => 0); + // Bonus typescript@next version + // const [reducerState, dispatch] = React.useReducer(reducer, true as const, arg => arg && initialState); + // Compile error in typescript@3.0 but not in typescript@3.1. + // const [reducerState, dispatch] = React.useReducer(reducer, true as true, arg => arg && initialState); + const [reducerState, dispatch] = React.useReducer(reducer, true as true, (arg: true): AppState => arg && initialState); + + const [, simpleDispatch] = React.useReducer(v => v + 1, 0); + + // inline object, to (manually) check if autocomplete works + React.useReducer(reducer, { age: 42, name: 'The Answer' }); + + // TODO (TypeScript 3.0): Decide whether implicit `any` should trigger `noImplicitAny` or if it should default to `unknown` or `never` + // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/52873 + // $ExpectType (value: any) => any + const anyCallback = React.useCallback(value => { + // $ExpectType any + return value; + }, []); + // $ExpectType any + anyCallback({}); + // $ExpectType (value: string) => number + const typedCallback = React.useCallback((value: string) => { + return Number(value); + }, []); + // $ExpectType number + typedCallback("1"); + // Argument of type '{}' is not assignable to parameter of type 'string'. + // $ExpectError + typedCallback({}); + + // test useRef and its convenience overloads + // $ExpectType MutableRefObject + 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 + React.useRef(null); + // $ExpectType MutableRefObject + React.useRef(undefined); + + // |null convenience overload + // it should _not_ be mutable if the generic argument doesn't include null + // $ExpectType RefObject + React.useRef(null); + // but it should be mutable if it does (i.e. is not the convenience overload) + // $ExpectType MutableRefObject + React.useRef(null); + + // |undefined convenience overload + // with no contextual type or generic argument it should default to undefined only (not {} or unknown!) + // $ExpectType MutableRefObject + React.useRef(); + // $ExpectType MutableRefObject + React.useRef(); + // don't just accept a potential undefined if there is a generic argument + // $ExpectError + React.useRef(undefined); + // make sure once again there's no |undefined if the initial value doesn't either + // $ExpectType MutableRefObject + React.useRef(1); + // and also that it is not getting erased if the parameter is wider + // $ExpectType MutableRefObject + React.useRef(1); + + // should be contextually typed + const a: React.MutableRefObject = React.useRef(undefined); + const b: React.MutableRefObject = React.useRef(); + const c: React.MutableRefObject = React.useRef(null); + const d: React.RefObject = React.useRef(null); + + const id = React.useMemo(() => Math.random(), []); + React.useImperativeHandle(ref, () => ({ id }), [id]); + // was named like this in the first alpha, renamed before release + // $ExpectError + React.useImperativeMethods(ref, () => ({}), [id]); + + // make sure again this is not going to the |null convenience overload + // $ExpectType MutableRefObject + const didLayout = React.useRef(false); + + React.useLayoutEffect(() => { + setState(1); + setState(prevState => prevState - 1); + didLayout.current = true; + }, []); + React.useEffect(() => { + dispatch({ type: 'getOlder' }); + // $ExpectError + dispatch(); + + simpleDispatch(); + setState(reducerState.age); + }, []); + + // effects are only allowed to either be actually void or return actually void functions + React.useEffect(() => () => {}); + // indistinguishable + React.useEffect(() => () => undefined); + // $ExpectError + React.useEffect(() => null); + // $ExpectError + React.useEffect(() => Math.random() ? null : undefined); + // $ExpectError + React.useEffect(() => () => null); + // $ExpectError + React.useEffect(() => () => Math.random() ? null : undefined); + // $ExpectError + React.useEffect(() => async () => {}); + // $ExpectError + React.useEffect(async () => () => {}); + + 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()[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(undefined)[0]; + // make sure useState does not widen + const [toggle, setToggle] = React.useState(false); + // $ExpectType boolean + toggle; + // make sure setState accepts a function + setToggle(r => !r); + + // useReducer convenience overload + + return React.useCallback(() => didLayout.current, []); +} + +const UsesEveryHook = React.forwardRef( + function UsesEveryHook(props: {}, ref?: React.Ref<{ id: number }>) { + // $ExpectType boolean + useEveryHook(ref)(); + + return null; + } +); +const everyHookRef = React.createRef<{ id: number }>(); +; + + { + // $ExpectType { id: number; } | null + ref; + }}/>; diff --git a/types/react/v17/test/index.ts b/types/react/v17/test/index.ts new file mode 100644 index 00000000000000..bec90d264c33b1 --- /dev/null +++ b/types/react/v17/test/index.ts @@ -0,0 +1,857 @@ +import * as React from "react"; +import * as ReactDOM from "react-dom"; +import * as ReactDOMServer from "react-dom/server"; +import * as PropTypes from "prop-types"; +import createFragment = require("react-addons-create-fragment"); +import * as LinkedStateMixin from "react-addons-linked-state-mixin"; +import * as PureRenderMixin from "react-addons-pure-render-mixin"; +import shallowCompare = require("react-addons-shallow-compare"); +import update = require("react-addons-update"); +import createReactClass = require("create-react-class"); +import * as DOM from "react-dom-factories"; + +// NOTE: forward declarations for tests +declare function setInterval(...args: any[]): any; +declare function clearInterval(...args: any[]): any; +declare var console: Console; +interface Console { + log(...args: any[]): void; +} + +interface Props { + hello: string; + world?: string | null | undefined; + foo: number; +} + +interface State { + inputValue?: string | null | undefined; + seconds?: number | undefined; +} + +interface Snapshot { + baz: string; +} + +interface Context { + someValue?: string | null | undefined; +} + +interface ChildContext { + someOtherValue: string; +} + +interface MyComponent extends React.Component { + reset(): void; +} + +// use any for ClassAttribute type sine we're using string refs +const props: Props & React.ClassAttributes = { + key: 42, + ref: "myComponent42", + hello: "world", + foo: 42 +}; + +const scProps: SCProps = { + foo: 42 +}; + +declare const container: Element; + +// +// Top-Level API +// -------------------------------------------------------------------------- +{ + interface State { + inputValue: string; + seconds: number; + } + class SettingStateFromCtorComponent extends React.Component { + constructor(props: Props) { + super(props); + // $ExpectError + this.state = { + inputValue: 'hello' + }; + } + render() { return null; } + } + + class BadlyInitializedState extends React.Component { + // $ExpectError -> this throws error on TS 2.6 uncomment once TS requirement is TS >= 2.7 + // state = { + // secondz: 0, + // inputValuez: 'hello' + // }; + render() { return null; } + } + class BetterPropsAndStateChecksComponent extends React.Component { + render() { return null; } + componentDidMount() { + // $ExpectError -> this will be true in next BC release where state is gonna be `null | Readonly` + console.log(this.state.inputValue); + } + mutateState() { + // $ExpectError + this.state = { + inputValue: 'hello' + }; + + // Even if state is not set, this is allowed by React + this.setState({ inputValue: 'hello' }); + this.setState((prevState, props) => { + // $ExpectError + props = { foo: 'nope' }; + // $ExpectError + props.foo = 'nope'; + + return { inputValue: prevState.inputValue + ' foo' }; + }); + } + mutateProps() { + // $ExpectError + this.props = {}; + // $ExpectError + this.props = { + key: 42, + ref: "myComponent42", + hello: "world", + foo: 42 + }; + } + } +} + +class ModernComponent extends React.Component + implements MyComponent, React.ChildContextProvider { + static propTypes: React.ValidationMap = { + hello: PropTypes.string.isRequired, + world: PropTypes.string, + foo: PropTypes.number.isRequired + }; + + static contextTypes: React.ValidationMap = { + someValue: PropTypes.string + }; + + static childContextTypes: React.ValidationMap = { + someOtherValue: PropTypes.string.isRequired + }; + + context: Context = {}; + + getChildContext() { + return { + someOtherValue: "foo" + }; + } + + state = { + inputValue: this.context.someValue, + seconds: this.props.foo + }; + + reset() { + this._myComponent.reset(); + this.setState({ + inputValue: this.context.someValue, + seconds: this.props.foo + }); + } + + private readonly _myComponent: MyComponent; + private _input: HTMLInputElement | null; + + render() { + return DOM.div(null, + DOM.input({ + ref: input => this._input = input, + value: this.state.inputValue ? this.state.inputValue : undefined + }), + DOM.input({ + onChange: event => console.log(event.target) + })); + } + + shouldComponentUpdate(nextProps: Props, nextState: State, nextContext: any): boolean { + return shallowCompare(this, nextProps, nextState); + } + + getSnapshotBeforeUpdate(prevProps: Readonly) { + return { baz: `${prevProps.foo}baz` }; + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: Snapshot) { + return; + } +} + +class ModernComponentArrayRender extends React.Component { + render() { + return [DOM.h1({ key: "1" }, "1"), + DOM.h1({ key: "2" }, "2")]; + } +} + +class ModernComponentNoState extends React.Component { } +class ModernComponentNoPropsAndState extends React.Component { } + +interface SCProps { + foo?: number | undefined; +} + +function FunctionComponent(props: SCProps) { + return props.foo ? DOM.div(null, props.foo) : null; +} + +// tslint:disable-next-line:no-namespace +namespace FunctionComponent { + export const displayName = "FunctionComponent"; + export const defaultProps = { foo: 42 }; +} + +const FunctionComponent2: React.FunctionComponent = + // props is contextually typed + props => DOM.div(null, props.foo); +FunctionComponent2.displayName = "FunctionComponent2"; +FunctionComponent2.defaultProps = { + foo: 42 +}; + +const LegacyStatelessComponent2: React.SFC = + // props is contextually typed + props => DOM.div(null, props.foo); +LegacyStatelessComponent2.displayName = "LegacyStatelessComponent2"; +LegacyStatelessComponent2.defaultProps = { + foo: 42 +}; + +const FunctionComponent3: React.FunctionComponent = + // allows usage of props.children + // allows null return + props => props.foo ? DOM.div(null, props.foo, props.children) : null; + +const LegacyStatelessComponent3: React.SFC = + // allows usage of props.children + // allows null return + props => props.foo ? DOM.div(null, props.foo, props.children) : null; + +// allows null as props +const FunctionComponent4: React.FunctionComponent = props => null; + +// undesired: Rejects `false` because of https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051 +// leaving here to document limitation and inspect error message +const FunctionComponent5: React.FunctionComponent = () => false; // $ExpectError + +// React.createFactory +const factory: React.CFactory = + React.createFactory(ModernComponent); +const factoryElement: React.CElement = + factory(props); + +const functionComponentFactory: React.FunctionComponentFactory = + React.createFactory(FunctionComponent); +const functionComponentFactoryElement: React.FunctionComponentElement = + functionComponentFactory(props); + +const legacyStatelessComponentFactory: React.SFCFactory = + React.createFactory(FunctionComponent); +const legacyStatelessComponentFactoryElement: React.SFCElement = + legacyStatelessComponentFactory(props); + +const domFactory: React.DOMFactory, Element> = + React.createFactory("div"); +const domFactoryElement: React.DOMElement, Element> = + domFactory(); + +// React.createElement +const element: React.CElement = React.createElement(ModernComponent, props); +const elementNoState: React.CElement = React.createElement(ModernComponentNoState, props); +const elementNullProps: React.CElement<{}, ModernComponentNoPropsAndState> = React.createElement(ModernComponentNoPropsAndState, null); +const functionComponentElement: React.FunctionComponentElement = React.createElement(FunctionComponent, scProps); +const functionComponentElementNullProps: React.FunctionComponentElement = React.createElement(FunctionComponent4, null); +const legacyStatelessComponentElement: React.SFCElement = React.createElement(FunctionComponent, scProps); +const legacyStatelessComponentElementNullProps: React.SFCElement = React.createElement(FunctionComponent4, null); +const domElement: React.DOMElement, HTMLDivElement> = React.createElement("div"); +const domElementNullProps = React.createElement("div", null); +const htmlElement = React.createElement("input", { type: "text" }); +const inputElementNullProps: React.DOMElement, HTMLInputElement> = React.createElement("input", null); +const svgElement = React.createElement("svg", { accentHeight: 12 }); +const svgElementNullProps = React.createElement("svg", null); +const fragmentElement: React.ReactElement<{}> = React.createElement(React.Fragment, {}, [React.createElement("div"), React.createElement("div")]); +const fragmentElementNullProps: React.ReactElement<{}> = React.createElement(React.Fragment, null, [React.createElement("div"), React.createElement("div")]); + +const customProps: React.HTMLProps = props; +const customDomElement = "my-element"; +const nonLiteralElement = React.createElement(customDomElement, customProps); +const customDomElementNullProps = React.createElement(customDomElement, null); + +// https://github.com/Microsoft/TypeScript/issues/15019 + +function foo3(child: React.ComponentClass<{ name: string }> | React.FunctionComponent<{ name: string }> | string) { + React.createElement(child, { name: "bar" }); +} + +function foo4(child: React.ComponentClass<{ name: string }> | React.SFC<{ name: string }> | string) { + React.createElement(child, { name: "bar" }); +} + +// React.cloneElement +const clonedElement: React.CElement = React.cloneElement(element, { foo: 43 }); + +React.cloneElement(element, {}); +React.cloneElement(element, {}, null); + +const clonedElement2: React.CElement = + React.cloneElement(element, { + ref: c => c && c.reset() + }); +const clonedElement3: React.CElement = + React.cloneElement(element, { + key: "8eac7", + foo: 55 + }); +const clonedfunctionComponentElement: React.FunctionComponentElement = + React.cloneElement(functionComponentElement, { foo: 44 }); +const clonedlegacyStatelessComponentElement: React.SFCElement = + React.cloneElement(legacyStatelessComponentElement, { foo: 44 }); +// Clone base DOMElement +const clonedDOMElement: React.DOMElement, HTMLDivElement> = + React.cloneElement(domElement, { + className: "clonedDOMElement" + }); +// Clone ReactHTMLElement +const clonedHtmlElement: React.ReactHTMLElement = + React.cloneElement(htmlElement, { + className: "clonedHTMLElement" + }); +// Clone ReactSVGElement +const clonedSvgElement: React.ReactSVGElement = + React.cloneElement(svgElement, { + className: "clonedVGElement" + }); + +// React.render +const component: ModernComponent = ReactDOM.render(element, container); +const componentNullContainer: ModernComponent = ReactDOM.render(element, null); + +const componentElementOrNull: ModernComponent = ReactDOM.render(element, container); +const componentNoState: ModernComponentNoState = ReactDOM.render(elementNoState, container); +const componentNoStateElementOrNull: ModernComponentNoState = ReactDOM.render(elementNoState, container); +const domComponent: Element = ReactDOM.render(domElement, container); + +// Other Top-Level API +const unmounted: boolean = ReactDOM.unmountComponentAtNode(container); +const str: string = ReactDOMServer.renderToString(element); +const markup: string = ReactDOMServer.renderToStaticMarkup(element); +const notValid: boolean = React.isValidElement(props); // false +const isValid = React.isValidElement(element); // true +let domNode = ReactDOM.findDOMNode(component); +domNode = ReactDOM.findDOMNode(domNode as Element); +const fragmentType: React.ComponentType = React.Fragment; + +// +// React Elements +// -------------------------------------------------------------------------- + +const type: React.ComponentClass = element.type; +const elementProps: Props = element.props; +const key = element.key; + +// +// Component API +// -------------------------------------------------------------------------- + +// modern +const componentState: State = component.state; +component.setState({ inputValue: "!!!" }); +component.forceUpdate(); + +const myComponent = component as MyComponent; +myComponent.reset(); + +// +// Refs +// -------------------------------------------------------------------------- + +interface RCProps { } + +class RefComponent extends React.Component { + static create = React.createFactory(RefComponent); + refMethod() { + } +} + +let componentRef: RefComponent | null = new RefComponent({}); +RefComponent.create({ ref: "componentRef" }); +// type of c should be inferred +RefComponent.create({ ref: c => componentRef = c }); +componentRef.refMethod(); + +let domNodeRef: Element | null; +DOM.div({ ref: "domRef" }); +// type of node should be inferred +DOM.div({ ref: node => domNodeRef = node }); + +let inputNodeRef: HTMLInputElement | null; +DOM.input({ ref: node => inputNodeRef = node as HTMLInputElement }); + +interface ForwardingRefComponentProps { + hello: string; + world?: string | null | undefined; + foo: number; +} + +const ForwardingRefComponent = React.forwardRef((props: ForwardingRefComponentProps, ref: React.Ref) => { + return React.createElement(RefComponent, { ref }); +}); + +// Declaring forwardRef render function separately (not inline). +const ForwardRefRenderFunction = (props: ForwardingRefComponentProps, ref: React.ForwardedRef) => { + return React.createElement(RefComponent, { ref }); +}; +React.forwardRef(ForwardRefRenderFunction); + +const ForwardingRefComponentPropTypes: React.WeakValidationMap = {}; +ForwardingRefComponent.propTypes = ForwardingRefComponentPropTypes; + +// render function tests +// need the explicit type declaration for typescript < 3.1 +const ForwardRefRenderFunctionWithPropTypes: { (): null, propTypes?: {} | undefined } = () => null; +// Warning: forwardRef render functions do not support propTypes or defaultProps +// $ExpectError +React.forwardRef(ForwardRefRenderFunctionWithPropTypes); + +const ForwardRefRenderFunctionWithDefaultProps: { (): null, defaultProps?: {} | undefined } = () => null; +// Warning: forwardRef render functions do not support propTypes or defaultProps +// $ExpectError +React.forwardRef(ForwardRefRenderFunctionWithDefaultProps); + +function RefCarryingComponent() { + const ref = React.createRef(); + // Without the explicit type argument, TypeScript infers `{ref: React.RefObject}` + // from the second argument because both of the inferences generated by the first argument + // (both to the `P` in the call signature and the `P` in `defaultProps`) have low priority. + // Then we get a type error because `ForwardingRefComponent.defaultProps` has the wrong type. + // Can/should this be fixed somehow? + return React.createElement & ForwardingRefComponentProps>( + ForwardingRefComponent, + { + ref, + hello: 'there', + foo: 0, + }, + ); +} +const ForwardingRefComponent2 = React.forwardRef((props, ref) => { + return React.createElement('div', { + ref(e: HTMLDivElement) { + if (typeof ref === 'function') { + ref(e); + } else if (ref) { + ref.current = e; + } + } + }); +}); + +const MemoizedForwardingRefComponent = React.memo(ForwardingRefComponent); +const LazyComponent = React.lazy(() => Promise.resolve({ default: RefComponent })); + +type ClassComponentAsRef = React.ElementRef; // $ExpectType RefComponent +type FunctionComponentWithoutPropsAsRef = React.ElementRef; // $ExpectType never +type FunctionComponentWithPropsAsRef = React.ElementRef; // $ExpectType never +type HTMLIntrinsicAsRef = React.ElementRef<'div'>; // $ExpectType HTMLDivElement +type SVGIntrinsicAsRef = React.ElementRef<'svg'>; // $ExpectType SVGSVGElement +type ForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent +type MemoizedForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent +type LazyComponentAsRef = React.ElementRef; // $ExpectType RefComponent + +// +// Attributes +// -------------------------------------------------------------------------- + +const children: any[] = ["Hello world", [null], DOM.span(null)]; +const divStyle: React.CSSProperties = { // CSSProperties + flex: "1 1 main-size", + backgroundImage: "url('hello.png')" +}; +const htmlAttr: React.HTMLProps = { + key: 36, + ref: "htmlComponent", + children, + className: "test-attr", + style: divStyle, + slot: "HTMLComponent", + onClick: (event: React.MouseEvent<{}>) => { + event.preventDefault(); + event.stopPropagation(); + }, + onClickCapture: (event: React.MouseEvent<{}>) => { + event.preventDefault(); + event.stopPropagation(); + }, + onAnimationStart: event => { + const currentTarget: EventTarget & HTMLElement = event.currentTarget; + }, + onBlur: (event: React.FocusEvent) => { + const { + // $ExpectType (EventTarget & Element) | null + relatedTarget, + // $ExpectType EventTarget & Element + target + } = event; + }, + onFocus: (event: React.FocusEvent) => { + const { + // $ExpectType (EventTarget & Element) | null + relatedTarget, + // $ExpectType EventTarget & Element + target + } = event; + }, + dangerouslySetInnerHTML: { + __html: "STRONG" + }, + unselectable: 'on', + 'aria-atomic': false, + 'aria-checked': 'true', + 'aria-colcount': 7, + 'aria-label': 'test', + 'aria-relevant': 'additions removals' +}; +DOM.div(htmlAttr); +DOM.span(htmlAttr); +DOM.input(htmlAttr); + +DOM.svg({ + viewBox: "0 0 48 48", + xmlns: "http://www.w3.org/2000/svg" +}, + DOM.rect({ + className: 'foobar', + id: 'foo', + color: 'black', + x: 22, + y: 10, + width: 4, + height: 28, + strokeDasharray: '30%', + strokeDashoffset: '20%' + }), + DOM.rect({ + x: 10, + y: 22, + width: 28, + height: 4, + strokeDasharray: 30, + strokeDashoffset: 20 + }), + DOM.path({ + d: "M0,0V3H3V0ZM1,1V2H2V1Z", + fill: "#999999", + fillRule: "evenodd" + }) +); + +// +// React.Children +// -------------------------------------------------------------------------- + +const mappedChildrenArray: number[] = + React.Children.map(children, (child: any) => 42); +const childrenArray: Array> = children; +const mappedChildrenArrayWithKnownChildren: number[] = + React.Children.map(childrenArray, (child) => child.props.p); +React.Children.forEach(children, (child) => { }); +const nChildren: number = React.Children.count(children); +let onlyChild: React.ReactElement = React.Children.only(DOM.div()); // ok +onlyChild = React.Children.only([null, [[["Hallo"], true]], false]); // error +const childrenToArray: Array> = React.Children.toArray(children); + +declare const numberChildren: number[]; +declare const nodeChildren: React.ReactNode; +declare const elementChildren: JSX.Element[]; +declare const mixedChildren: Array; +declare const singlePluralChildren: JSX.Element | JSX.Element[]; +declare const renderPropsChildren: () => JSX.Element; + +// $ExpectType null +const mappedChildrenArray0 = React.Children.map(null, num => num); +// $ExpectType undefined +const mappedChildrenArray1 = React.Children.map(undefined, num => num); +// $ExpectType number[] +const mappedChildrenArray2 = React.Children.map(numberChildren, num => num); +// $ExpectType Element[] +const mappedChildrenArray3 = React.Children.map(elementChildren, element => element); +// $ExpectType (string | Element)[] +const mappedChildrenArray4 = React.Children.map(mixedChildren, elementOrString => elementOrString); +// This test uses a conditional type because otherwise it gets flaky and can resolve to either Key or ReactText, both +// of which are aliases for `string | number`. +const mappedChildrenArray5 = React.Children.map(singlePluralChildren, element => element.key); +// $ExpectType true +type mappedChildrenArray5Type = typeof mappedChildrenArray5 extends React.Key[] ? true : false; +// $ExpectType string[] +const mappedChildrenArray6 = React.Children.map(renderPropsChildren, element => element.name); +// The return type may not be an array +// $ExpectError +const mappedChildrenArray7 = React.Children.map(nodeChildren, node => node).map; + +// +// Example from http://facebook.github.io/react/ +// -------------------------------------------------------------------------- + +interface TimerState { + secondsElapsed: number; +} +class Timer extends React.Component<{}, TimerState> { + state = { + secondsElapsed: 0 + }; + private _interval: number; + tick() { + this.setState((prevState, props) => ({ + secondsElapsed: prevState.secondsElapsed + 1 + })); + } + componentDidMount() { + this._interval = setInterval(() => this.tick(), 1000); + } + componentWillUnmount() { + clearInterval(this._interval); + } + render() { + return DOM.div( + null, + "Seconds Elapsed: ", + this.state.secondsElapsed + ); + } +} +ReactDOM.render(React.createElement(Timer), container); + +// +// createFragment addon +// -------------------------------------------------------------------------- +createFragment({ + a: DOM.div(), + b: ["a", false, React.createElement("span")] +}); + +// +// LinkedStateMixin addon +// -------------------------------------------------------------------------- +createReactClass({ + mixins: [LinkedStateMixin], + getInitialState() { + return { + isChecked: false, + message: "hello!" + }; + }, + render() { + return DOM.div(null, + DOM.input({ + type: "checkbox", + checkedLink: this.linkState("isChecked") + }), + DOM.input({ + type: "text", + valueLink: this.linkState("message") + }) + ); + } +}); + +// +// PureRenderMixin addon +// -------------------------------------------------------------------------- +createReactClass({ + mixins: [PureRenderMixin], + render() { return DOM.div(null); } +}); + +// +// update addon +// -------------------------------------------------------------------------- +{ + // These are copied from https://facebook.github.io/react/docs/update.html + const initialArray = [1, 2, 3]; + const newArray = update(initialArray, { $push: [4] }); // => [1, 2, 3, 4] + + const collection = [1, 2, { a: [12, 17, 15] }]; + const newCollection = update(collection, { 2: { a: { $splice: [[1, 1, 13, 14]] } } }); + // => [1, 2, {a: [12, 13, 14, 15]}] + + const obj = { a: 5, b: 3 }; + const newObj = update(obj, { + b: { + $apply: (x) => x * 2 + } + }); + // => {a: 5, b: 6} + const newObj2 = update(obj, { b: { $set: obj.b * 2 } }); + + const objShallow = { a: 5, b: 3 }; + const newObjShallow = update(obj, { $merge: { b: 6, c: 7 } }); // => {a: 5, b: 6, c: 7} +} + +// +// Events +// -------------------------------------------------------------------------- +function eventHandler(e: T) {} + +function handler(e: React.MouseEvent) { + eventHandler(e); +} + +const keyboardExtendsUI: React.UIEventHandler = (e: React.KeyboardEvent) => {}; + +// +// The SyntheticEvent.target.value should be accessible for onChange +// -------------------------------------------------------------------------- +class SyntheticEventTargetValue extends React.Component<{}, { value: string }> { + state: { value: string }; + constructor(props: {}) { + super(props); + this.state = { value: 'a' }; + } + render() { + return DOM.textarea({ + value: this.state.value, + onChange: e => { + const target: HTMLTextAreaElement = e.target; + } + }); + } +} + +DOM.input({ + onChange: event => { + // `event.target` is guaranteed to be HTMLInputElement + const target: HTMLInputElement = event.target; + } +}); + +// A ChangeEvent is a valid FormEvent (maintain compatibility with existing +// event handlers) + +type InputChangeEvent = React.ChangeEvent; +type InputFormEvent = React.FormEvent; +const changeEvent: InputChangeEvent = undefined as any; +const formEvent: InputFormEvent = changeEvent; + +// defaultProps should be optional of props +{ + interface ComponentProps { + prop1: string; + prop2: string; + prop3?: string | undefined; + } + class ComponentWithDefaultProps extends React.Component { + static defaultProps = { + prop3: "default value", + }; + } + const VariableWithAClass: React.ComponentClass = ComponentWithDefaultProps; +} + +// complex React.DOMElement type +declare var x: React.DOMElement<{ + className: string; + style: { + height: string; + overflowY: "auto"; + transition: string; + }; +}, Element>; + +// React 16 should be able to render its children directly +class RenderChildren extends React.Component { + render() { + const { children } = this.props; + return children !== undefined ? children : null; + } +} + +const Memoized1 = React.memo(function Foo(props: { foo: string }) { return null; }); +React.createElement(Memoized1, { foo: 'string' }); + +const Memoized2 = React.memo<{ bar: string }>( + function Bar(props: { bar: string }) { return null; }, + (prevProps, nextProps) => prevProps.bar === nextProps.bar +); +React.createElement(Memoized2, { bar: 'string' }); + +const specialSfc1: React.ExoticComponent = Memoized1; +const functionComponent: React.FunctionComponent = Memoized2; +const sfc: React.SFC = Memoized2; +// this $ExpectError is failing on TypeScript@next +// // $ExpectError Property '$$typeof' is missing in type +// const specialSfc2: React.SpecialSFC = props => null; + +const propsWithChildren: React.PropsWithChildren = { + hello: "world", + foo: 42, + children: React.createElement(functionComponent), +}; + +type UnionProps = + | ({ type: 'single'; value?: number } & React.RefAttributes) + | ({ type: 'multiple'; value?: number[] } & React.RefAttributes); + +// $ExpectError +const propsWithoutRef: React.PropsWithoutRef = { + type: 'single', + value: [2], +}; + +// JSXElemenConstructor vs Component assignability +{ + interface ExactProps { + value: 'A' | 'B'; + } + interface NarrowerProps { + value: 'A'; + } + interface WiderProps { + value: 'A' | 'B' | 'C'; + } + + // We don't actually care about the concrete type of `Wrapper` i.e. + // we don't care about the value created by `new Wrapper()`. + // We only care about the props we can pass to the component. + let Wrapper: React.JSXElementConstructor; + // $ExpectError + Wrapper = class Narrower extends React.Component {}; + // $ExpectError + Wrapper = (props: NarrowerProps) => null; + Wrapper = class Exact extends React.Component {}; + Wrapper = (props: ExactProps) => null; + Wrapper = class Wider extends React.Component {}; + Wrapper = (props: WiderProps) => null; + + React.createElement(Wrapper, { value: 'A' }); + React.createElement(Wrapper, { value: 'B' }); + // $ExpectError + React.createElement(Wrapper, { value: 'C' }); +} + +// ComponentPropsWithRef and JSXElementConstructor +{ + interface Props { + value: string; + } + type InferredProps = React.ComponentPropsWithRef>; + const props: Props = { + value: 'inferred', + // $ExpectError + notImplemented: 5 + }; + const inferredProps: InferredProps = { + value: 'inferred', + // $ExpectError + notImplemented: 5 + }; +} diff --git a/types/react/v17/test/managedAttributes.tsx b/types/react/v17/test/managedAttributes.tsx new file mode 100644 index 00000000000000..494545c2100da4 --- /dev/null +++ b/types/react/v17/test/managedAttributes.tsx @@ -0,0 +1,276 @@ +interface LeaveMeAloneDtslint { foo: string; } +// // Re-enable when we move @types/react to TS 3.0 + +// import * as React from 'react'; +// import * as PropTypes from 'prop-types'; + +// interface Props { +// bool?: boolean; +// fnc: () => any; +// node?: React.ReactNode; +// num?: number; +// reqNode: NonNullable; +// str: string; +// } + +// const propTypes = { +// bool: PropTypes.bool, +// fnc: PropTypes.func.isRequired, +// node: PropTypes.node, +// num: PropTypes.number, +// str: PropTypes.string.isRequired, +// extraStr: PropTypes.string.isRequired, +// extraNum: PropTypes.number +// }; + +// const defaultProps = { +// fnc: (() => 'abc') as () => any, +// extraBool: false, +// reqNode: 'text_node' as NonNullable +// }; + +// class AnnotatedPropTypesAndDefaultProps extends React.Component { +// static propTypes = propTypes; +// static defaultProps = defaultProps; +// } + +// const annotatedPropTypesAndDefaultPropsTests = [ +// // $ExpectError +// , // str and extraStr are required +// , +// // $ExpectError +// , // num type mismatch +// } +// num={0} +// reqNode={} +// str='abc' +// /> +// ]; + +// class UnannotatedPropTypesAndDefaultProps extends React.Component { +// static propTypes = propTypes; +// static defaultProps = defaultProps; +// } + +// const unannotatedPropTypesAndDefaultPropsTests = [ +// // $ExpectError +// , // stra and extraStr are required +// , +// // $ExpectError +// , // extraBool type mismatch +// } +// num={0} +// reqNode={} +// str='abc' +// /> +// ]; + +// class AnnotatedPropTypes extends React.Component { +// static propTypes = propTypes; +// } + +// const annotatedPropTypesTests = [ +// // $ExpectError +// , // str, extraStr, reqNode and fnc are required +// } />, +// // $ExpectError +// } extraBool={false} />, // extraBool doesn't exist +// // $ExpectError +// } num='abc' />, // num type mismatch +// } +// num={0} +// reqNode={} +// str='abc' +// /> +// ]; + +// class UnannotatedPropTypes extends React.Component { +// static propTypes = propTypes; +// } + +// const unannotatedPropTypesTests = [ +// // $ExpectError +// , // str, extraStr and fnc are required +// , +// // $ExpectError +// } />, // reqNode doesn't exist +// // $ExpectError +// } num='abc' />, // num type mismatch +// } +// num={0} +// str='abc' +// /> +// ]; + +// class AnnotatedDefaultProps extends React.Component { +// static defaultProps = defaultProps; +// } + +// const annotatedDefaultPropsTests = [ +// // $ExpectError +// , // str is required +// , +// } />, +// // $ExpectError +// { }} />, // str type mismatch +// +// ]; + +// class UnannotatedDefaultProps extends React.Component { +// static defaultProps = defaultProps; +// } + +// const unannotatedDefaultPropsTests = [ +// , +// } +// /> +// ]; + +// class ComponentWithNoDefaultProps extends React.Component {} + +// function FunctionalComponent(props: Props) { return <>{props.reqNode} } +// FunctionalComponent.defaultProps = defaultProps; + +// const functionalComponentTests = [ +// // $ExpectError +// , +// // This is possibly a bug/limitation of TS +// // Even if JSX.LibraryManagedAttributes returns the correct type, it doesn't seem to work with non-classes +// // This also doesn't work with things typed React.SFC

because defaultProps will always be Partial

+// // $ExpectError +// +// ]; + +// const MemoFunctionalComponent = React.memo(FunctionalComponent); +// const MemoAnnotatedDefaultProps = React.memo(AnnotatedDefaultProps); +// const LazyMemoFunctionalComponent = React.lazy(async () => ({ default: MemoFunctionalComponent })); +// const LazyMemoAnnotatedDefaultProps = React.lazy(async () => ({ default: MemoAnnotatedDefaultProps })); + +// const memoTests = [ +// // $ExpectError +// , +// // $ExpectError won't work as long as FunctionalComponent doesn't work either +// , +// // $ExpectError +// , +// , +// // $ExpectError this doesn't work despite JSX.LibraryManagedAttributes returning the correct type +// , +// // $ExpectError won't work as long as FunctionalComponent doesn't work either +// , +// // $ExpectError +// , +// // $ExpectError this doesn't work despite JSX.LibraryManagedAttributes returning the correct type +// +// ]; + +// type AnnotatedDefaultPropsLibraryManagedAttributes = JSX.LibraryManagedAttributes; +// // $ExpectType AnnotatedDefaultPropsLibraryManagedAttributes +// type FunctionalComponentLibraryManagedAttributes = JSX.LibraryManagedAttributes; +// // $ExpectType FunctionalComponentLibraryManagedAttributes +// type MemoFunctionalComponentLibraryManagedAttributes = JSX.LibraryManagedAttributes; +// // $ExpectType FunctionalComponentLibraryManagedAttributes +// type LazyMemoFunctionalComponentLibraryManagedAttributes = JSX.LibraryManagedAttributes; + +// const ForwardRef = React.forwardRef((props: Props, ref: React.Ref) => ( +// +// )); +// ForwardRef.defaultProps = defaultProps; + +// const forwardRefTests = [ +// // $ExpectError +// , +// } +// str='' +// />, +// // same bug as MemoFunctionalComponent and React.SFC-typed things +// // $ExpectError the type of ForwardRef.defaultProps stays Partial

anyway even if assigned +// +// ]; + +// const weakComponentPropTypes = { +// foo: PropTypes.string, +// bar: PropTypes.bool.isRequired +// }; +// interface WeakComponentProps1 { +// foo: any; +// bar: number; +// } +// interface WeakComponentProps2 { +// foo: string; +// bar: any; +// } +// interface WeakComponentProps3 { +// foo: any; +// bar: any; +// } + +// // $ExpectType true +// type weakComponentTest1 = JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, any> extends { +// foo?: string | null +// bar: boolean +// } ? true : false; +// // $ExpectType true +// type weakComponentTest2 = JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakComponentProps1> extends { +// foo?: string | null +// bar: number +// } ? true : false; +// // $ExpectType true +// type weakComponentTest3 = JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakComponentProps2> extends { +// foo: string +// bar: boolean +// } ? true : false; + +// // $ExpectError +// const weakComponentOptionalityTest1: JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakComponentProps3> = { foo: '' }; +// const weakComponentOptionalityTest2: JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakComponentProps3> = { bar: true }; + +// interface IndexedComponentProps { +// [K: string]: boolean; +// } +// interface WeakIndexedComponentProps { +// [K: string]: any; +// } + +// const weakComponentIndexedTest1: JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, IndexedComponentProps> = { }; +// // $ExpectError +// const weakComponentIndexedTest2: JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, IndexedComponentProps> = { foo: '' }; +// const weakComponentIndexedTest3: JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakIndexedComponentProps> = { foo: '' }; +// const weakComponentIndexedTest4: JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakIndexedComponentProps> = { foo: 4 }; + +// const optionalUnionPropTest: JSX.LibraryManagedAttributes<{ propTypes: {} }, { optional?: string } | { optional?: number }> = {}; diff --git a/types/react/v17/test/tsx.tsx b/types/react/v17/test/tsx.tsx new file mode 100644 index 00000000000000..60b7884ffa4e40 --- /dev/null +++ b/types/react/v17/test/tsx.tsx @@ -0,0 +1,527 @@ +import PropTypes = require("prop-types"); +import React = require("react"); + +interface SCProps { + foo?: number | undefined; +} +const FunctionComponent: React.FunctionComponent = ({ foo }: SCProps) => { + return

; +}; +FunctionComponent.displayName = "FunctionComponent3"; +FunctionComponent.defaultProps = { + foo: 42 +}; +; +; + +const FunctionComponent2: React.FunctionComponent = ({ foo, children }) => { + return
{foo}{children}
; +}; +FunctionComponent2.displayName = "FunctionComponent4"; +FunctionComponent2.defaultProps = { + foo: 42 +}; +24; + +const VoidFunctionComponent: React.VoidFunctionComponent = ({ foo }: SCProps) => { + return
{foo}
; +}; +VoidFunctionComponent.displayName = "VoidFunctionComponent1"; +VoidFunctionComponent.defaultProps = { + foo: 42 +}; +; + +// $ExpectError +const VoidFunctionComponent2: React.VoidFunctionComponent = ({ foo, children }) => { + return
{foo}{children}
; +}; +VoidFunctionComponent2.displayName = "VoidFunctionComponent2"; +VoidFunctionComponent2.defaultProps = { + foo: 42 +}; +24; // $ExpectError + +// svg sanity check + + + + Hello, world! + + +
Hello again!
+ +; + +// React-specific Attributes +
+ foo +
; + +// WAI-ARIA 1.1 Attributes +
+ bar +
; + +// button type attribute +; +; +; +; // $ExpectError +; // $ExpectError + +interface Props { + hello: string; +} +interface State { + foobar: string; +} +class ComponentWithPropsAndState extends React.Component { +} +; + +class ComponentWithoutState extends React.Component { +} +; + +class ComponentWithoutPropsAndState extends React.Component { +} +; + +const FunctionComponentWithoutProps: React.FunctionComponent = (props) => { + return
; +}; +; + +// React.createContext +const ContextWithRenderProps = React.createContext('defaultValue'); + +// unstable APIs should not be part of the typings +// $ExpectError +const ContextUsingUnstableObservedBits = React.createContext(undefined, (previous, next) => 7); +// $ExpectError + + {(value: unknown) => null} +; + +// Fragments +
+ + + Child 1 + Child 2 + + + Child 3 + Child 4 + + +
; + +// Strict Mode +
+ +
+ +
; + +// Below tests that setState() works properly for both regular and callback modes +class SetStateTest extends React.Component<{}, { foo: boolean, bar: boolean }> { + handleSomething = () => { + this.setState({ foo: '' }); // $ExpectError + this.setState({ foo: true }); + this.setState({ foo: true, bar: true }); + this.setState({}); + this.setState(null); + this.setState({ foo: true, foo2: true }); // $ExpectError + this.setState(() => ({ foo: '' })); // $ExpectError + this.setState(() => ({ foo: true })); + this.setState(() => ({ foo: true, bar: true })); + this.setState(() => ({ foo: true, foo2: true })); // $ExpectError + this.setState(() => ({ foo: '', foo2: true })); // $ExpectError + this.setState(() => ({ })); // ok! + this.setState({ foo: true, bar: undefined}); // $ExpectError + this.setState(prevState => (prevState.bar ? { bar: false } : null)); + } +} + +// Below tests that extended types for state work +export abstract class SetStateTestForExtendsState extends React.Component { + foo() { + this.setState({ baseProp: 'foobar' }); + } +} + +// Below tests that & generic still works +// This is invalid because 'S' may specify a different type for `baseProp`. +// export abstract class SetStateTestForAndedState extends React.Component { +// foo() { +// this.setState({ baseProp: 'foobar' }); +// } +// } + +interface NewProps { foo: string; } +interface NewState { bar: string; } + +class ComponentWithNewLifecycles extends React.Component { + static getDerivedStateFromProps: React.GetDerivedStateFromProps = (nextProps) => { + return { bar: `${nextProps.foo}bar` }; + } + + state = { + bar: 'foo' + }; + + getSnapshotBeforeUpdate(prevProps: Readonly) { + return { baz: `${prevProps.foo}baz` }; + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: { baz: string }) { + return; + } + + render() { + return this.state.bar; + } +} +; + +class PureComponentWithNewLifecycles extends React.PureComponent { + static getDerivedStateFromProps: React.GetDerivedStateFromProps = (nextProps) => { + return { bar: `${nextProps.foo}bar` }; + } + + state = { + bar: 'foo' + }; + + getSnapshotBeforeUpdate(prevProps: Readonly) { + return { baz: `${prevProps.foo}baz` }; + } + + componentDidUpdate(prevProps: Readonly, prevState: Readonly, snapshot: { baz: string }) { + return; + } + + render() { + return this.state.bar; + } +} +; + +class ComponentWithLargeState extends React.Component<{}, Record<'a'|'b'|'c', string>> { + static getDerivedStateFromProps: React.GetDerivedStateFromProps<{}, Record<'a'|'b'|'c', string>> = () => { + return { a: 'a' }; + } +} +const AssignedComponentWithLargeState: React.ComponentClass = ComponentWithLargeState; + +const componentWithBadLifecycle = new (class extends React.Component<{}, {}, number> {})({}); +componentWithBadLifecycle.getSnapshotBeforeUpdate = () => { // $ExpectError + return 'number'; +}; +componentWithBadLifecycle.componentDidUpdate = (prevProps: {}, prevState: {}, snapshot?: string) => { // $ExpectError + return; +}; + +const Memoized1 = React.memo(function Foo(props: { foo: string }) { return null; }); +; + +const Memoized2 = React.memo( + function Bar(props: { bar: string }) { return null; }, + (prevProps, nextProps) => prevProps.bar === nextProps.bar +); +; + +const Memoized3 = React.memo(class Test extends React.Component<{ x?: string | undefined }> {}); + { if (ref) { ref.props.x; } }}/>; + +const memoized4Ref = React.createRef(); +const Memoized4 = React.memo(React.forwardRef((props: {}, ref: React.Ref) =>
)); +; + +const Memoized5 = React.memo<{ test: boolean }>( + prop => <>{prop.test && prop.children}, + (prevProps, nextProps) => nextProps.test ? prevProps.children === nextProps.children : prevProps.test +); + +; + +const Memoized6: React.NamedExoticComponent = React.memo(props => null); +; +// $ExpectError +; + +// NOTE: this test _requires_ TypeScript 3.1 +// It is passing, for what it's worth. +// const Memoized7 = React.memo((() => { +// function HasDefaultProps(props: { test: boolean }) { return null; } +// HasDefaultProps.defaultProps = { +// test: true +// }; +// return HasDefaultProps; +// })()); +// // $ExpectType boolean +// Memoized7.type.defaultProps.test; + +const LazyClassComponent = React.lazy(async () => ({ default: ComponentWithPropsAndState })); +const LazyMemoized3 = React.lazy(async () => ({ default: Memoized3 })); +const LazyRefForwarding = React.lazy(async () => ({ default: Memoized4 })); + +}> + + { if (ref) { ref.props.hello; } }} hello='test'/> + { if (ref) { ref.props.x; } }}/> + +; + +; +// $ExpectError +; + +// unstable API should not be part of the typings +// $ExpectError +; + +class LegacyContext extends React.Component { + static contextTypes = { foo: PropTypes.node.isRequired }; + + render() { + // $ExpectType any + this.context.foo; + return this.context.foo; + } +} + +class LegacyContextAnnotated extends React.Component { + static contextTypes = { foo: PropTypes.node.isRequired }; + context: { foo: React.ReactNode } = { foo: {} as React.ReactNode }; + + render() { + // $ExpectType ReactNode + this.context.foo; + return this.context.foo; + } +} + +class NewContext extends React.Component { + static contextType = ContextWithRenderProps; + context: React.ContextType = ""; + + render() { + // $ExpectType string + this.context; + return this.context; + } +} + +const ForwardRef = React.forwardRef((props: JSX.IntrinsicElements['div'], ref?: React.Ref) =>
); +const ForwardRef2 = React.forwardRef((props: React.ComponentProps, ref?: React.Ref) => ); +const divFnRef = (ref: HTMLDivElement|null) => { /* empty */ }; +const divRef = React.createRef(); + +; +; +; // $ExpectError +; +; +; // $ExpectError + +const htmlElementFnRef = (instance: HTMLElement | null) => {}; +const htmlElementRef = React.createRef(); +
; +
; +// `current` is nullable +const unsoundDivFnRef = (current: HTMLDivElement) => {}; +declare const unsoundDivObjectRef: { current: HTMLDivElement }; +// `current` is nullable but type-checks +// this is consistent with ref objects +// If this ever not type-checks, `
; +
; + +const newContextRef = React.createRef(); +; +; + +const ForwardNewContext = React.forwardRef((_props: {}, ref?: React.Ref) => ); +; +; // $ExpectError + +const ForwardRef3 = React.forwardRef( + (props: JSX.IntrinsicElements['div'] & Pick, ref?: React.Ref) => +
+); + +; +; + +const { Profiler } = React; + +// 'id' is missing +; // $ExpectError +// 'onRender' is missing +; // $ExpectError +// 'number' is not assignable to 'string' +; // $ExpectError + + { + const message = `${id} ${phase} took ${actualDuration.toFixed(2)}s actual, ${baseDuration.toFixed(2)}s base`; + + const commitMessage = `commit started ${startTime.toFixed(2)} within ${commitTime}`; + + const interactionsSummary = Array.from(interactions) + .map(interaction => { + return `${interaction.id}: '${interaction.name}' started at ${interaction.timestamp.toFixed(2)}`; + }) + .join("\n"); + const interactionMessage = `there were ${interactions.size} interactions:\n${interactionsSummary}`; + }} +> +
+; + +type ImgProps = React.ComponentProps<'img'>; +const imgProps: ImgProps = {}; +// the order of the strings in the union seems to vary +// with the typescript version, so test assignment instead +imgProps.decoding = 'async'; +imgProps.decoding = 'auto'; +imgProps.decoding = 'sync'; +imgProps.loading = 'eager'; +imgProps.loading = 'lazy'; +// $ExpectError +imgProps.loading = 'nonsense'; +// $ExpectError +imgProps.decoding = 'nonsense'; +type ImgPropsWithRef = React.ComponentPropsWithRef<'img'>; +// $ExpectType ((instance: HTMLImageElement | null) => void) | RefObject | null | undefined +type ImgPropsWithRefRef = ImgPropsWithRef['ref']; +type ImgPropsWithoutRef = React.ComponentPropsWithoutRef<'img'>; +// $ExpectType false +type ImgPropsHasRef = 'ref' extends keyof ImgPropsWithoutRef ? true : false; + +const HasClassName: React.ReactType<{ className?: string | undefined }> = 'a'; +const HasFoo: React.ReactType<{ foo: boolean }> = 'a'; // $ExpectError +const HasFoo2: React.ReactType<{ foo: boolean }> = (props: { foo: boolean }) => null; +const HasFoo3: React.ReactType<{ foo: boolean }> = (props: { foo: string }) => null; // $ExpectError +const HasHref: React.ReactType<{ href?: string | undefined }> = 'a'; +const HasHref2: React.ReactType<{ href?: string | undefined }> = 'div'; // $ExpectError + +const CustomElement: React.ReactType = 'my-undeclared-element'; // $ExpectError + +// custom elements now need to be declared as intrinsic elements +declare global { + namespace JSX { + interface IntrinsicElements { + 'my-declared-element': {}; + } + } +} + +const CustomElement2: React.ReactType = 'my-declared-element'; + +interface TestPropTypesProps { + foo: string; +} +interface TestPropTypesProps1 { + foo?: string | undefined; +} +interface TestPropTypesProps2 { + foo: string | null; +} +interface TestPropTypesProps3 { + foo?: string | null | undefined; +} +const testPropTypes = { + foo: PropTypes.string +}; +type DeclaredPropTypes

= Required['propTypes'], undefined>>; +// $ExpectType false +type propTypesTest = typeof testPropTypes extends DeclaredPropTypes ? true : false; +// $ExpectType true +type propTypesTest1 = typeof testPropTypes extends DeclaredPropTypes ? true : false; +// $ExpectType true +type propTypesTest2 = typeof testPropTypes extends DeclaredPropTypes ? true : false; +// $ExpectType true +type propTypesTest3 = typeof testPropTypes extends DeclaredPropTypes ? true : false; +function CustomSelect(props: { + children: ReadonlyArray< + React.ReactElement< + React.ComponentPropsWithoutRef + > + >; + }): JSX.Element { + return ( +

+
    {props.children}
+ +
+ ); +} +function CustomSelectOption(props: { + value: string; + children: React.ReactNode; +}): JSX.Element { + return
  • {props.children}
  • ; +} +function Example() { + return ( + + One + Two + + ); +} + +function reactNodeTests() { + function *createChildren() { + yield
    one
    ; + yield
    two
    ; + } + +
    {Object.freeze([
    one
    ,
    two
    ])}
    ; +
    {new Set([
    one
    ,
    two
    ])}
    ; + // TODO: This warns at runtime so we should probably reject it as well +
    + { + new Map([ + ['one',
    one
    ], + ['two',
    two
    ], + ]) + } +
    ; +
    {createChildren()}
    ; +} diff --git a/types/react/v17/tsconfig.json b/types/react/v17/tsconfig.json new file mode 100644 index 00000000000000..7e96f14a8af628 --- /dev/null +++ b/types/react/v17/tsconfig.json @@ -0,0 +1,34 @@ +{ + "files": [ + "index.d.ts", + "test/index.ts", + "test/tsx.tsx", + "test/cssProperties.tsx", + "test/elementAttributes.tsx", + "test/managedAttributes.tsx", + "test/hooks.tsx" + ], + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "paths": { + "react": ["react/v17"], + "react-dom": ["react-dom/v17"], + "react-dom/*": ["react-dom/v17/*"] + }, + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "baseUrl": "../../", + "typeRoots": [ + "../../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true, + "jsx": "preserve" + } +} diff --git a/types/react/v17/tslint.json b/types/react/v17/tslint.json new file mode 100644 index 00000000000000..a77da0b0c4ae6b --- /dev/null +++ b/types/react/v17/tslint.json @@ -0,0 +1,11 @@ +{ + "extends": "@definitelytyped/dtslint/dt.json", + "rules": { + "dt-header": false, + "no-empty-interface": false, + "no-object-literal-type-assertion": false, + "no-unnecessary-generics": false, + "strict-export-declare-modifiers": false, + "void-return": false + } +} diff --git a/types/styled-components/index.d.ts b/types/styled-components/index.d.ts index 510e9a562dcb06..b4141ca6284840 100644 --- a/types/styled-components/index.d.ts +++ b/types/styled-components/index.d.ts @@ -80,20 +80,9 @@ export type StyledComponentProps< ? WithOptionalTheme< MakeAttrsOptional & MakeAttrsOptional, T - > & - WithChildrenIfReactComponentClass + > : never; -// Because of React typing quirks, when getting props from a React.ComponentClass, -// we need to manually add a `children` field. -// See https://github.com/DefinitelyTyped/DefinitelyTyped/pull/31945 -// and https://github.com/DefinitelyTyped/DefinitelyTyped/pull/32843 -type WithChildrenIfReactComponentClass> = C extends React.ComponentClass< - any -> - ? { children?: React.ReactNode | undefined } - : {}; - type StyledComponentPropsWithAs< C extends string | React.ComponentType, T extends object, @@ -116,11 +105,11 @@ export type InterpolationFunction

    = (props: P) => Interpolation

    ; type Attrs, T> = ((props: ThemedStyledProps) => A) | A; -export type ThemedGlobalStyledClassProps = WithOptionalTheme & { +export type ThemedGlobalStyledClassProps

    = WithOptionalTheme & { suppressMultiMountWarning?: boolean | undefined; }; -export interface GlobalStyleComponent extends React.ComponentClass> {} +export interface GlobalStyleComponent

    extends React.ComponentClass> {} // remove the call signature from StyledComponent so Interpolation can still infer InterpolationFunction type StyledComponentInterpolation = diff --git a/types/styled-components/test/index.tsx b/types/styled-components/test/index.tsx index 4c1ac577dc5bff..d441dff4f3d11f 100644 --- a/types/styled-components/test/index.tsx +++ b/types/styled-components/test/index.tsx @@ -1143,8 +1143,7 @@ export class WrapperClass extends React.Component { } } const StyledWrapperClass = styled(WrapperClass)``; -// React.Component typings always add `children` to props, so this should accept children -const wrapperClass = Text; +const wrapperClass = Text; // $ExpectError export class WrapperClassFuncChild extends React.Component any }> { render() { @@ -1152,7 +1151,6 @@ export class WrapperClassFuncChild extends React.Component{() => 'text'}; const wrapperClassNoChildren = Text; // $ExpectError diff --git a/types/styled-theming/styled-theming-tests.ts b/types/styled-theming/styled-theming-tests.tsx similarity index 92% rename from types/styled-theming/styled-theming-tests.ts rename to types/styled-theming/styled-theming-tests.tsx index 23d83ce65c80d1..0f6635d8b773d1 100644 --- a/types/styled-theming/styled-theming-tests.ts +++ b/types/styled-theming/styled-theming-tests.tsx @@ -68,5 +68,7 @@ const Button = styled.button` const elementWithoutProp = React.createElement(Button); const elementWithCorrectProp = React.createElement(Button, { kind: 'secondary' }); -// $ExpectError +// TODO(react): Unclear why this doesn't error. Probably picking an overload where excess props are fine. const element = React.createElement(Button, { kind: 'wrong variant' }); +// $ExpectError +