Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions types/react/experimental.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ declare const UNDEFINED_VOID_ONLY: unique symbol;
type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never };

declare module '.' {
// Need an interface to not cause ReactNode to be a self-referential type.
interface PromiseLikeOfReactNode extends PromiseLike<ReactNode> {}
interface DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES {
promises: PromiseLikeOfReactNode;
}

export interface SuspenseProps {
/**
* The presence of this prop indicates that the content is computationally expensive to render.
Expand Down
18 changes: 17 additions & 1 deletion types/react/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,23 @@ declare namespace React {
*/
interface ReactNodeArray extends ReadonlyArray<ReactNode> {}
type ReactFragment = Iterable<ReactNode>;
type ReactNode = ReactElement | string | number | ReactFragment | ReactPortal | boolean | null | undefined;

/**
* For internal usage only.
* Different release channels declare additional types of ReactNode this particular release channel accepts.
* App or library types should never augment this interface.
*/
interface DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES {}
type ReactNode =
| ReactElement
| string
| number
| ReactFragment
| ReactPortal
| boolean
| null
| undefined
| DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES];

//
// Top Level API
Expand Down
32 changes: 32 additions & 0 deletions types/react/test/experimental.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,35 @@ function Optimistic() {
addToOptimisticCartTyped2(String(item));
};
}

// ReactNode tests
{
// @ts-expect-error
const render: React.ReactNode = () => React.createElement('div');
// @ts-expect-error
const emptyObject: React.ReactNode = { };
// @ts-expect-error
const plainObject: React.ReactNode = { dave: true };
const promise: React.ReactNode = Promise.resolve('React');
// @ts-expect-error plain objects are not allowed
<div>{{ dave: true }}</div>;
<div>{Promise.resolve('React')}</div>;
}

function elementTypeTests() {
const ReturnPromise = () => Promise.resolve('React');
// @ts-expect-error Needs https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135
const FCPromise: React.FC = ReturnPromise;
class RenderPromise extends React.Component {
render() {
return Promise.resolve('React');
}
}

// @ts-expect-error Needs https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135
<ReturnPromise />;
// @ts-expect-error Needs https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135
React.createElement(ReturnPromise);
<RenderPromise />;
React.createElement(RenderPromise);
}
9 changes: 9 additions & 0 deletions types/react/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,15 @@ class RenderChildren extends React.Component<{ children?: React.ReactNode }> {
// But no return results in `void`.
// @ts-expect-error
const noReturn: React.ReactNode = ['a', 'b'].map(label => {});

// @ts-expect-error
const render: React.ReactNode = () => React.createElement('div');
// @ts-expect-error
const emptyObject: React.ReactNode = { };
// @ts-expect-error
const plainObject: React.ReactNode = { dave: true };
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
const promise: React.ReactNode = Promise.resolve('React');
}

const Memoized1 = React.memo(function Foo(props: { foo: string }) { return null; });
Expand Down
23 changes: 23 additions & 0 deletions types/react/test/tsx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,10 @@ function reactNodeTests() {
}
</div>;
<div>{createChildren()}</div>;
// @ts-expect-error plain objects are not allowed
<div>{{ dave: true }}</div>;
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
<div>{Promise.resolve('React')}</div>;
}

function elementTypeTests() {
Expand Down Expand Up @@ -656,6 +660,16 @@ function elementTypeTests() {
}
}

const ReturnPromise = () => Promise.resolve('React');
// @ts-expect-error experimental release channel only
const FCPromise: React.FC = ReturnPromise;
class RenderPromise extends React.Component {
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
render() {
return Promise.resolve('React');
}
}

// Desired behavior.
// @ts-expect-error
<ReturnVoid />;
Expand Down Expand Up @@ -727,6 +741,15 @@ function elementTypeTests() {
React.createElement(ReturnReactNode);
<RenderReactNode />;
React.createElement(RenderReactNode);

// @ts-expect-error Only available in experimental release channel
<ReturnPromise />;
// @ts-expect-error Only available in experimental release channel
React.createElement(ReturnPromise);
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
<RenderPromise />;
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
React.createElement(RenderPromise);
}

function managingRefs() {
Expand Down
6 changes: 6 additions & 0 deletions types/react/ts5.0/experimental.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ declare const UNDEFINED_VOID_ONLY: unique symbol;
type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never };

declare module '.' {
// Need an interface to not cause ReactNode to be a self-referential type.
interface PromiseLikeOfReactNode extends PromiseLike<ReactNode> {}
interface DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES {
promises: PromiseLikeOfReactNode;
}

export interface SuspenseProps {
/**
* The presence of this prop indicates that the content is computationally expensive to render.
Expand Down
18 changes: 17 additions & 1 deletion types/react/ts5.0/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,23 @@ declare namespace React {
*/
interface ReactNodeArray extends ReadonlyArray<ReactNode> {}
type ReactFragment = Iterable<ReactNode>;
type ReactNode = ReactElement | string | number | ReactFragment | ReactPortal | boolean | null | undefined;

/**
* For internal usage only.
* Different release channels declare additional types of ReactNode this particular release channel accepts.
* App or library types should never augment this interface.
*/
interface DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES {}
type ReactNode =
| ReactElement
| string
| number
| ReactFragment
| ReactPortal
| boolean
| null
| undefined
| DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES[keyof DO_NOT_USE_OR_YOU_WILL_BE_FIRED_EXPERIMENTAL_REACT_NODES];

//
// Top Level API
Expand Down
32 changes: 32 additions & 0 deletions types/react/ts5.0/test/experimental.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,35 @@ function Optimistic() {
addToOptimisticCartTyped2(String(item));
};
}

// ReactNode tests
{
// @ts-expect-error
const render: React.ReactNode = () => React.createElement('div');
// @ts-expect-error
const emptyObject: React.ReactNode = { };
// @ts-expect-error
const plainObject: React.ReactNode = { dave: true };
const promise: React.ReactNode = Promise.resolve('React');
// @ts-expect-error plain objects are not allowed
<div>{{ dave: true }}</div>;
<div>{Promise.resolve('React')}</div>;
}

function elementTypeTests() {
const ReturnPromise = () => Promise.resolve('React');
// @ts-expect-error Needs https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135
const FCPromise: React.FC = ReturnPromise;
class RenderPromise extends React.Component {
render() {
return Promise.resolve('React');
}
}

// @ts-expect-error Needs https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135
<ReturnPromise />;
// @ts-expect-error Needs https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65135
React.createElement(ReturnPromise);
<RenderPromise />;
React.createElement(RenderPromise);
}
9 changes: 9 additions & 0 deletions types/react/ts5.0/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,15 @@ class RenderChildren extends React.Component<{ children?: React.ReactNode }> {
// But no return results in `void`.
// @ts-expect-error
const noReturn: React.ReactNode = ['a', 'b'].map(label => {});

// @ts-expect-error
const render: React.ReactNode = () => React.createElement('div');
// @ts-expect-error
const emptyObject: React.ReactNode = { };
// @ts-expect-error
const plainObject: React.ReactNode = { dave: true };
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
const promise: React.ReactNode = Promise.resolve('React');
}

const Memoized1 = React.memo(function Foo(props: { foo: string }) { return null; });
Expand Down
23 changes: 23 additions & 0 deletions types/react/ts5.0/test/tsx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,10 @@ function reactNodeTests() {
}
</div>;
<div>{createChildren()}</div>;
// @ts-expect-error plain objects are not allowed
<div>{{ dave: true }}</div>;
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
<div>{Promise.resolve('React')}</div>;
}

function elementTypeTests() {
Expand Down Expand Up @@ -656,6 +660,16 @@ function elementTypeTests() {
}
}

const ReturnPromise = () => Promise.resolve('React');
// @ts-expect-error experimental release channel only
const FCPromise: React.FC = ReturnPromise;
class RenderPromise extends React.Component {
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
render() {
return Promise.resolve('React');
}
}

// Desired behavior.
// @ts-expect-error
<ReturnVoid />;
Expand Down Expand Up @@ -727,6 +741,15 @@ function elementTypeTests() {
React.createElement(ReturnReactNode);
<RenderReactNode />;
React.createElement(RenderReactNode);

// @ts-expect-error Only available in experimental release channel
<ReturnPromise />;
// @ts-expect-error Only available in experimental release channel
React.createElement(ReturnPromise);
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
<RenderPromise />;
// Will not type-check in a real project but accepted in DT tests since experimental.d.ts is part of compilation.
React.createElement(RenderPromise);
}

function managingRefs() {
Expand Down