RFC: Consult new JSX.ElementType for valid JSX element types (#51328)

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Sebastian Silbermann
2023-04-14 19:54:02 +02:00
committed by GitHub
parent b798e6bfa5
commit b92483f20c
12 changed files with 1289 additions and 2 deletions

View File

@@ -30351,7 +30351,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
/**
* Returns true iff React would emit this tag name as a string rather than an identifier or qualified name
*/
function isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression): boolean {
function isJsxIntrinsicIdentifier(tagName: JsxTagNameExpression): tagName is Identifier {
return tagName.kind === SyntaxKind.Identifier && isIntrinsicJsxName(tagName.escapedText);
}
@@ -30816,6 +30816,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
function getJsxElementTypeTypeAt(location: Node): Type | undefined {
const type = getJsxType(JsxNames.ElementType, location);
if (isErrorType(type)) return undefined;
return type;
}
/**
* Returns all the properties of the Jsx.IntrinsicElements interface
*/
@@ -30884,7 +30890,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const jsxOpeningLikeNode = node ;
const sig = getResolvedSignature(jsxOpeningLikeNode);
checkDeprecatedSignature(sig, node);
checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(jsxOpeningLikeNode), getReturnTypeOfSignature(sig), jsxOpeningLikeNode);
const elementTypeConstraint = getJsxElementTypeTypeAt(jsxOpeningLikeNode);
if (elementTypeConstraint !== undefined) {
const tagName = jsxOpeningLikeNode.tagName;
const tagType = isJsxIntrinsicIdentifier(tagName)
? getStringLiteralType(unescapeLeadingUnderscores(tagName.escapedText))
: checkExpression(tagName);
checkTypeRelatedTo(tagType, elementTypeConstraint, assignableRelation, tagName, Diagnostics.Its_type_0_is_not_a_valid_JSX_element_type, () => {
const componentName = getTextOfNode(tagName);
return chainDiagnosticMessages(/*details*/ undefined, Diagnostics._0_cannot_be_used_as_a_JSX_component, componentName);
});
}
else {
checkJsxReturnAssignableToAppropriateBound(getJsxReferenceKind(jsxOpeningLikeNode), getReturnTypeOfSignature(sig), jsxOpeningLikeNode);
}
}
}
@@ -48823,6 +48843,7 @@ namespace JsxNames {
export const ElementAttributesPropertyNameContainer = "ElementAttributesProperty" as __String; // TODO: Deprecate and remove support
export const ElementChildrenAttributeNameContainer = "ElementChildrenAttribute" as __String;
export const Element = "Element" as __String;
export const ElementType = "ElementType" as __String;
export const IntrinsicAttributes = "IntrinsicAttributes" as __String;
export const IntrinsicClassAttributes = "IntrinsicClassAttributes" as __String;
export const LibraryManagedAttributes = "LibraryManagedAttributes" as __String;

View File

@@ -7760,5 +7760,9 @@
"Non-abstract class '{0}' does not implement all abstract members of '{1}'": {
"category": "Error",
"code": 18052
},
"Its type '{0}' is not a valid JSX element type.": {
"category": "Error",
"code": 18053
}
}

View File

@@ -0,0 +1,200 @@
tests/cases/compiler/jsxElementType.tsx(34,2): error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
tests/cases/compiler/jsxElementType.tsx(36,16): error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
tests/cases/compiler/jsxElementType.tsx(40,2): error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
tests/cases/compiler/jsxElementType.tsx(42,15): error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
tests/cases/compiler/jsxElementType.tsx(46,2): error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
tests/cases/compiler/jsxElementType.tsx(48,15): error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
tests/cases/compiler/jsxElementType.tsx(52,2): error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
tests/cases/compiler/jsxElementType.tsx(54,14): error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
tests/cases/compiler/jsxElementType.tsx(59,2): error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
tests/cases/compiler/jsxElementType.tsx(61,16): error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
tests/cases/compiler/jsxElementType.tsx(70,2): error TS2769: No overload matches this call.
Overload 1 of 2, '(props: Readonly<{ title: string; }>): RenderStringClass', gave the following error.
Property 'title' is missing in type '{}' but required in type 'Readonly<{ title: string; }>'.
Overload 2 of 2, '(props: { title: string; }, context?: any): RenderStringClass', gave the following error.
Property 'title' is missing in type '{}' but required in type 'Readonly<{ title: string; }>'.
tests/cases/compiler/jsxElementType.tsx(72,20): error TS2769: No overload matches this call.
Overload 1 of 2, '(props: Readonly<{ title: string; }>): RenderStringClass', gave the following error.
Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
Overload 2 of 2, '(props: { title: string; }, context?: any): RenderStringClass', gave the following error.
Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
tests/cases/compiler/jsxElementType.tsx(78,1): error TS2339: Property 'boop' does not exist on type 'JSX.IntrinsicElements'.
tests/cases/compiler/jsxElementType.tsx(79,1): error TS2339: Property 'my-undeclared-custom-element' does not exist on type 'JSX.IntrinsicElements'.
tests/cases/compiler/jsxElementType.tsx(91,2): error TS2786: 'ReactNativeFlatList' cannot be used as a JSX component.
Its type '(props: {}, ref: ForwardedRef<typeof ReactNativeFlatList>) => null' is not a valid JSX element type.
Type '(props: {}, ref: ForwardedRef<typeof ReactNativeFlatList>) => null' is not assignable to type '(props: any) => React18ReactNode'.
Target signature provides too few arguments. Expected 2 or more, but got 1.
tests/cases/compiler/jsxElementType.tsx(95,11): error TS2322: Type '{}' is not assignable to type 'LibraryManagedAttributes<T, {}>'.
tests/cases/compiler/jsxElementType.tsx(98,2): error TS2304: Cannot find name 'Unresolved'.
tests/cases/compiler/jsxElementType.tsx(99,2): error TS2304: Cannot find name 'Unresolved'.
==== tests/cases/compiler/jsxElementType.tsx (18 errors) ====
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
type React18ReactFragment = ReadonlyArray<React18ReactNode>;
type React18ReactNode =
| React.ReactElement<any>
| string
| number
| React18ReactFragment
| React.ReactPortal
| boolean
| null
| undefined
| Promise<React18ReactNode>;
// // React.JSXElementConstructor but it now can return React nodes from function components.
type NewReactJSXElementConstructor<P> =
| ((props: P) => React18ReactNode)
| (new (props: P) => React.Component<P, any>);
declare global {
namespace JSX {
type ElementType = string | NewReactJSXElementConstructor<any>;
interface IntrinsicElements {
['my-custom-element']: React.DOMAttributes<unknown>;
}
}
}
let Component: NewReactJSXElementConstructor<{ title: string }>;
const RenderElement = ({ title }: { title: string }) => <div>{title}</div>;
Component = RenderElement;
<RenderElement />;
~~~~~~~~~~~~~
!!! error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:32:37: 'title' is declared here.
<RenderElement title="react" />;
<RenderElement excessProp />;
~~~~~~~~~~
!!! error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
!!! error TS2322: Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
const RenderString = ({ title }: { title: string }) => title;
Component = RenderString;
<RenderString />;
~~~~~~~~~~~~
!!! error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:38:36: 'title' is declared here.
<RenderString title="react" />;
<RenderString excessProp />;
~~~~~~~~~~
!!! error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
!!! error TS2322: Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
const RenderNumber = ({ title }: { title: string }) => title.length;
Component = RenderNumber;
<RenderNumber />;
~~~~~~~~~~~~
!!! error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:44:36: 'title' is declared here.
<RenderNumber title="react" />;
<RenderNumber excessProp />;
~~~~~~~~~~
!!! error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
!!! error TS2322: Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
const RenderArray = ({ title }: { title: string }) => [title];
Component = RenderArray;
<RenderArray />;
~~~~~~~~~~~
!!! error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:50:35: 'title' is declared here.
<RenderArray title="react" />;
<RenderArray excessProp />;
~~~~~~~~~~
!!! error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
!!! error TS2322: Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
// React Server Component
const RenderPromise = async ({ title }: { title: string }) => "react";
Component = RenderPromise;
<RenderPromise />;
~~~~~~~~~~~~~
!!! error TS2741: Property 'title' is missing in type '{}' but required in type '{ title: string; }'.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:57:43: 'title' is declared here.
<RenderPromise title="react" />;
<RenderPromise excessProp />;
~~~~~~~~~~
!!! error TS2322: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & { title: string; }'.
!!! error TS2322: Property 'excessProp' does not exist on type 'IntrinsicAttributes & { title: string; }'.
// Class components still work
class RenderStringClass extends React.Component<{ title: string }> {
render() {
return this.props.title;
}
}
Component = RenderStringClass;
<RenderStringClass />;
~~~~~~~~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(props: Readonly<{ title: string; }>): RenderStringClass', gave the following error.
!!! error TS2769: Property 'title' is missing in type '{}' but required in type 'Readonly<{ title: string; }>'.
!!! error TS2769: Overload 2 of 2, '(props: { title: string; }, context?: any): RenderStringClass', gave the following error.
!!! error TS2769: Property 'title' is missing in type '{}' but required in type 'Readonly<{ title: string; }>'.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:64:51: 'title' is declared here.
!!! related TS2728 tests/cases/compiler/jsxElementType.tsx:64:51: 'title' is declared here.
<RenderStringClass title="react" />;
<RenderStringClass excessProp />;
~~~~~~~~~~
!!! error TS2769: No overload matches this call.
!!! error TS2769: Overload 1 of 2, '(props: Readonly<{ title: string; }>): RenderStringClass', gave the following error.
!!! error TS2769: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
!!! error TS2769: Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
!!! error TS2769: Overload 2 of 2, '(props: { title: string; }, context?: any): RenderStringClass', gave the following error.
!!! error TS2769: Type '{ excessProp: true; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
!!! error TS2769: Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<RenderStringClass> & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'.
// Host element types still work
<div />;
<my-custom-element />;
// Undeclared host element types are still rejected
<boop />;
~~~~~~~~
!!! error TS2339: Property 'boop' does not exist on type 'JSX.IntrinsicElements'.
<my-undeclared-custom-element />;
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2339: Property 'my-undeclared-custom-element' does not exist on type 'JSX.IntrinsicElements'.
// Highlighting various ecosystem compat issues
// react-native-gesture-handler
// https://github.com/software-mansion/react-native-gesture-handler/blob/79017e5e7cc2e82e6467851f870920ff836ee04f/src/components/GestureComponents.tsx#L139-L146
interface ReactNativeFlatListProps<Item> {}
function ReactNativeFlatList(
props: {},
ref: React.ForwardedRef<typeof ReactNativeFlatList>
) {
return null;
}
<ReactNativeFlatList />;
~~~~~~~~~~~~~~~~~~~
!!! error TS2786: 'ReactNativeFlatList' cannot be used as a JSX component.
!!! error TS2786: Its type '(props: {}, ref: ForwardedRef<typeof ReactNativeFlatList>) => null' is not a valid JSX element type.
!!! error TS2786: Type '(props: {}, ref: ForwardedRef<typeof ReactNativeFlatList>) => null' is not assignable to type '(props: any) => React18ReactNode'.
!!! error TS2786: Target signature provides too few arguments. Expected 2 or more, but got 1.
// testing higher-order component compat
function f1<T extends (props: {}) => React.ReactElement<any>>(Component: T) {
return <Component />;
~~~~~~~~~
!!! error TS2322: Type '{}' is not assignable to type 'LibraryManagedAttributes<T, {}>'.
}
<Unresolved />;
~~~~~~~~~~
!!! error TS2304: Cannot find name 'Unresolved'.
<Unresolved foo="abc" />;
~~~~~~~~~~
!!! error TS2304: Cannot find name 'Unresolved'.

View File

@@ -0,0 +1,233 @@
//// [jsxElementType.tsx]
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
type React18ReactFragment = ReadonlyArray<React18ReactNode>;
type React18ReactNode =
| React.ReactElement<any>
| string
| number
| React18ReactFragment
| React.ReactPortal
| boolean
| null
| undefined
| Promise<React18ReactNode>;
// // React.JSXElementConstructor but it now can return React nodes from function components.
type NewReactJSXElementConstructor<P> =
| ((props: P) => React18ReactNode)
| (new (props: P) => React.Component<P, any>);
declare global {
namespace JSX {
type ElementType = string | NewReactJSXElementConstructor<any>;
interface IntrinsicElements {
['my-custom-element']: React.DOMAttributes<unknown>;
}
}
}
let Component: NewReactJSXElementConstructor<{ title: string }>;
const RenderElement = ({ title }: { title: string }) => <div>{title}</div>;
Component = RenderElement;
<RenderElement />;
<RenderElement title="react" />;
<RenderElement excessProp />;
const RenderString = ({ title }: { title: string }) => title;
Component = RenderString;
<RenderString />;
<RenderString title="react" />;
<RenderString excessProp />;
const RenderNumber = ({ title }: { title: string }) => title.length;
Component = RenderNumber;
<RenderNumber />;
<RenderNumber title="react" />;
<RenderNumber excessProp />;
const RenderArray = ({ title }: { title: string }) => [title];
Component = RenderArray;
<RenderArray />;
<RenderArray title="react" />;
<RenderArray excessProp />;
// React Server Component
const RenderPromise = async ({ title }: { title: string }) => "react";
Component = RenderPromise;
<RenderPromise />;
<RenderPromise title="react" />;
<RenderPromise excessProp />;
// Class components still work
class RenderStringClass extends React.Component<{ title: string }> {
render() {
return this.props.title;
}
}
Component = RenderStringClass;
<RenderStringClass />;
<RenderStringClass title="react" />;
<RenderStringClass excessProp />;
// Host element types still work
<div />;
<my-custom-element />;
// Undeclared host element types are still rejected
<boop />;
<my-undeclared-custom-element />;
// Highlighting various ecosystem compat issues
// react-native-gesture-handler
// https://github.com/software-mansion/react-native-gesture-handler/blob/79017e5e7cc2e82e6467851f870920ff836ee04f/src/components/GestureComponents.tsx#L139-L146
interface ReactNativeFlatListProps<Item> {}
function ReactNativeFlatList(
props: {},
ref: React.ForwardedRef<typeof ReactNativeFlatList>
) {
return null;
}
<ReactNativeFlatList />;
// testing higher-order component compat
function f1<T extends (props: {}) => React.ReactElement<any>>(Component: T) {
return <Component />;
}
<Unresolved />;
<Unresolved foo="abc" />;
//// [jsxElementType.js]
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference path="react16.d.ts" />
var React = require("react");
var Component;
var RenderElement = function (_a) {
var title = _a.title;
return React.createElement("div", null, title);
};
Component = RenderElement;
React.createElement(RenderElement, null);
React.createElement(RenderElement, { title: "react" });
React.createElement(RenderElement, { excessProp: true });
var RenderString = function (_a) {
var title = _a.title;
return title;
};
Component = RenderString;
React.createElement(RenderString, null);
React.createElement(RenderString, { title: "react" });
React.createElement(RenderString, { excessProp: true });
var RenderNumber = function (_a) {
var title = _a.title;
return title.length;
};
Component = RenderNumber;
React.createElement(RenderNumber, null);
React.createElement(RenderNumber, { title: "react" });
React.createElement(RenderNumber, { excessProp: true });
var RenderArray = function (_a) {
var title = _a.title;
return [title];
};
Component = RenderArray;
React.createElement(RenderArray, null);
React.createElement(RenderArray, { title: "react" });
React.createElement(RenderArray, { excessProp: true });
// React Server Component
var RenderPromise = function (_a) {
var title = _a.title;
return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_b) {
return [2 /*return*/, "react"];
}); });
};
Component = RenderPromise;
React.createElement(RenderPromise, null);
React.createElement(RenderPromise, { title: "react" });
React.createElement(RenderPromise, { excessProp: true });
// Class components still work
var RenderStringClass = /** @class */ (function (_super) {
__extends(RenderStringClass, _super);
function RenderStringClass() {
return _super !== null && _super.apply(this, arguments) || this;
}
RenderStringClass.prototype.render = function () {
return this.props.title;
};
return RenderStringClass;
}(React.Component));
Component = RenderStringClass;
React.createElement(RenderStringClass, null);
React.createElement(RenderStringClass, { title: "react" });
React.createElement(RenderStringClass, { excessProp: true });
// Host element types still work
React.createElement("div", null);
React.createElement("my-custom-element", null);
// Undeclared host element types are still rejected
React.createElement("boop", null);
React.createElement("my-undeclared-custom-element", null);
function ReactNativeFlatList(props, ref) {
return null;
}
React.createElement(ReactNativeFlatList, null);
// testing higher-order component compat
function f1(Component) {
return React.createElement(Component, null);
}
React.createElement(Unresolved, null);
React.createElement(Unresolved, { foo: "abc" });

View File

@@ -0,0 +1,274 @@
=== tests/cases/compiler/jsxElementType.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
type React18ReactFragment = ReadonlyArray<React18ReactNode>;
>React18ReactFragment : Symbol(React18ReactFragment, Decl(jsxElementType.tsx, 1, 31))
>ReadonlyArray : Symbol(ReadonlyArray, Decl(lib.es5.d.ts, --, --))
>React18ReactNode : Symbol(React18ReactNode, Decl(jsxElementType.tsx, 3, 60))
type React18ReactNode =
>React18ReactNode : Symbol(React18ReactNode, Decl(jsxElementType.tsx, 3, 60))
| React.ReactElement<any>
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>ReactElement : Symbol(React.ReactElement, Decl(react16.d.ts, 135, 9))
| string
| number
| React18ReactFragment
>React18ReactFragment : Symbol(React18ReactFragment, Decl(jsxElementType.tsx, 1, 31))
| React.ReactPortal
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>ReactPortal : Symbol(React.ReactPortal, Decl(react16.d.ts, 172, 9))
| boolean
| null
| undefined
| Promise<React18ReactNode>;
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --))
>React18ReactNode : Symbol(React18ReactNode, Decl(jsxElementType.tsx, 3, 60))
// // React.JSXElementConstructor but it now can return React nodes from function components.
type NewReactJSXElementConstructor<P> =
>NewReactJSXElementConstructor : Symbol(NewReactJSXElementConstructor, Decl(jsxElementType.tsx, 13, 30))
>P : Symbol(P, Decl(jsxElementType.tsx, 16, 35))
| ((props: P) => React18ReactNode)
>props : Symbol(props, Decl(jsxElementType.tsx, 17, 6))
>P : Symbol(P, Decl(jsxElementType.tsx, 16, 35))
>React18ReactNode : Symbol(React18ReactNode, Decl(jsxElementType.tsx, 3, 60))
| (new (props: P) => React.Component<P, any>);
>props : Symbol(props, Decl(jsxElementType.tsx, 18, 10))
>P : Symbol(P, Decl(jsxElementType.tsx, 16, 35))
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>P : Symbol(P, Decl(jsxElementType.tsx, 16, 35))
declare global {
>global : Symbol(global, Decl(jsxElementType.tsx, 18, 48))
namespace JSX {
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementType.tsx, 20, 16))
type ElementType = string | NewReactJSXElementConstructor<any>;
>ElementType : Symbol(ElementType, Decl(jsxElementType.tsx, 21, 17))
>NewReactJSXElementConstructor : Symbol(NewReactJSXElementConstructor, Decl(jsxElementType.tsx, 13, 30))
interface IntrinsicElements {
>IntrinsicElements : Symbol(IntrinsicElements, Decl(react16.d.ts, 2514, 86), Decl(jsxElementType.tsx, 22, 67))
['my-custom-element']: React.DOMAttributes<unknown>;
>['my-custom-element'] : Symbol(IntrinsicElements['my-custom-element'], Decl(jsxElementType.tsx, 23, 33))
>'my-custom-element' : Symbol(IntrinsicElements['my-custom-element'], Decl(jsxElementType.tsx, 23, 33))
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>DOMAttributes : Symbol(React.DOMAttributes, Decl(react16.d.ts, 844, 9))
}
}
}
let Component: NewReactJSXElementConstructor<{ title: string }>;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>NewReactJSXElementConstructor : Symbol(NewReactJSXElementConstructor, Decl(jsxElementType.tsx, 13, 30))
>title : Symbol(title, Decl(jsxElementType.tsx, 29, 46))
const RenderElement = ({ title }: { title: string }) => <div>{title}</div>;
>RenderElement : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 31, 24))
>title : Symbol(title, Decl(jsxElementType.tsx, 31, 35))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
>title : Symbol(title, Decl(jsxElementType.tsx, 31, 24))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
Component = RenderElement;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>RenderElement : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5))
<RenderElement />;
>RenderElement : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5))
<RenderElement title="react" />;
>RenderElement : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 34, 14))
<RenderElement excessProp />;
>RenderElement : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5))
>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 35, 14))
const RenderString = ({ title }: { title: string }) => title;
>RenderString : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 37, 23))
>title : Symbol(title, Decl(jsxElementType.tsx, 37, 34))
>title : Symbol(title, Decl(jsxElementType.tsx, 37, 23))
Component = RenderString;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>RenderString : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5))
<RenderString />;
>RenderString : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5))
<RenderString title="react" />;
>RenderString : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 40, 13))
<RenderString excessProp />;
>RenderString : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5))
>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 41, 13))
const RenderNumber = ({ title }: { title: string }) => title.length;
>RenderNumber : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 43, 23))
>title : Symbol(title, Decl(jsxElementType.tsx, 43, 34))
>title.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
>title : Symbol(title, Decl(jsxElementType.tsx, 43, 23))
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
Component = RenderNumber;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>RenderNumber : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5))
<RenderNumber />;
>RenderNumber : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5))
<RenderNumber title="react" />;
>RenderNumber : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 46, 13))
<RenderNumber excessProp />;
>RenderNumber : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5))
>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 47, 13))
const RenderArray = ({ title }: { title: string }) => [title];
>RenderArray : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 49, 22))
>title : Symbol(title, Decl(jsxElementType.tsx, 49, 33))
>title : Symbol(title, Decl(jsxElementType.tsx, 49, 22))
Component = RenderArray;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>RenderArray : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5))
<RenderArray />;
>RenderArray : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5))
<RenderArray title="react" />;
>RenderArray : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 52, 12))
<RenderArray excessProp />;
>RenderArray : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5))
>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 53, 12))
// React Server Component
const RenderPromise = async ({ title }: { title: string }) => "react";
>RenderPromise : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 56, 30))
>title : Symbol(title, Decl(jsxElementType.tsx, 56, 41))
Component = RenderPromise;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>RenderPromise : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5))
<RenderPromise />;
>RenderPromise : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5))
<RenderPromise title="react" />;
>RenderPromise : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5))
>title : Symbol(title, Decl(jsxElementType.tsx, 59, 14))
<RenderPromise excessProp />;
>RenderPromise : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5))
>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 60, 14))
// Class components still work
class RenderStringClass extends React.Component<{ title: string }> {
>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29))
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>title : Symbol(title, Decl(jsxElementType.tsx, 63, 49))
render() {
>render : Symbol(RenderStringClass.render, Decl(jsxElementType.tsx, 63, 68))
return this.props.title;
>this.props.title : Symbol(title, Decl(jsxElementType.tsx, 63, 49))
>this.props : Symbol(React.Component.props, Decl(react16.d.ts, 367, 32))
>this : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29))
>props : Symbol(React.Component.props, Decl(react16.d.ts, 367, 32))
>title : Symbol(title, Decl(jsxElementType.tsx, 63, 49))
}
}
Component = RenderStringClass;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 29, 3))
>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29))
<RenderStringClass />;
>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29))
<RenderStringClass title="react" />;
>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29))
>title : Symbol(title, Decl(jsxElementType.tsx, 70, 18))
<RenderStringClass excessProp />;
>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29))
>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 71, 18))
// Host element types still work
<div />;
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
<my-custom-element />;
>my-custom-element : Symbol(JSX.IntrinsicElements['my-custom-element'], Decl(jsxElementType.tsx, 23, 33))
// Undeclared host element types are still rejected
<boop />;
<my-undeclared-custom-element />;
// Highlighting various ecosystem compat issues
// react-native-gesture-handler
// https://github.com/software-mansion/react-native-gesture-handler/blob/79017e5e7cc2e82e6467851f870920ff836ee04f/src/components/GestureComponents.tsx#L139-L146
interface ReactNativeFlatListProps<Item> {}
>ReactNativeFlatListProps : Symbol(ReactNativeFlatListProps, Decl(jsxElementType.tsx, 78, 33))
>Item : Symbol(Item, Decl(jsxElementType.tsx, 83, 35))
function ReactNativeFlatList(
>ReactNativeFlatList : Symbol(ReactNativeFlatList, Decl(jsxElementType.tsx, 83, 43))
props: {},
>props : Symbol(props, Decl(jsxElementType.tsx, 84, 29))
ref: React.ForwardedRef<typeof ReactNativeFlatList>
>ref : Symbol(ref, Decl(jsxElementType.tsx, 85, 12))
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>ForwardedRef : Symbol(React.ForwardedRef, Decl(react16.d.ts, 2355, 9))
>ReactNativeFlatList : Symbol(ReactNativeFlatList, Decl(jsxElementType.tsx, 83, 43))
) {
return null;
}
<ReactNativeFlatList />;
>ReactNativeFlatList : Symbol(ReactNativeFlatList, Decl(jsxElementType.tsx, 83, 43))
// testing higher-order component compat
function f1<T extends (props: {}) => React.ReactElement<any>>(Component: T) {
>f1 : Symbol(f1, Decl(jsxElementType.tsx, 90, 24))
>T : Symbol(T, Decl(jsxElementType.tsx, 93, 12))
>props : Symbol(props, Decl(jsxElementType.tsx, 93, 23))
>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6))
>ReactElement : Symbol(React.ReactElement, Decl(react16.d.ts, 135, 9))
>Component : Symbol(Component, Decl(jsxElementType.tsx, 93, 62))
>T : Symbol(T, Decl(jsxElementType.tsx, 93, 12))
return <Component />;
>Component : Symbol(Component, Decl(jsxElementType.tsx, 93, 62))
}
<Unresolved />;
<Unresolved foo="abc" />;
>foo : Symbol(foo, Decl(jsxElementType.tsx, 98, 11))

View File

@@ -0,0 +1,292 @@
=== tests/cases/compiler/jsxElementType.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : typeof React
type React18ReactFragment = ReadonlyArray<React18ReactNode>;
>React18ReactFragment : readonly React18ReactNode[]
type React18ReactNode =
>React18ReactNode : string | number | boolean | React.ReactElement<any> | React.ReactPortal | React18ReactFragment | Promise<React18ReactNode> | null | undefined
| React.ReactElement<any>
>React : any
| string
| number
| React18ReactFragment
| React.ReactPortal
>React : any
| boolean
| null
| undefined
| Promise<React18ReactNode>;
// // React.JSXElementConstructor but it now can return React nodes from function components.
type NewReactJSXElementConstructor<P> =
>NewReactJSXElementConstructor : NewReactJSXElementConstructor<P>
| ((props: P) => React18ReactNode)
>props : P
| (new (props: P) => React.Component<P, any>);
>props : P
>React : any
declare global {
>global : any
namespace JSX {
type ElementType = string | NewReactJSXElementConstructor<any>;
>ElementType : string | NewReactJSXElementConstructor<any>
interface IntrinsicElements {
['my-custom-element']: React.DOMAttributes<unknown>;
>['my-custom-element'] : React.DOMAttributes<unknown>
>'my-custom-element' : "my-custom-element"
>React : any
}
}
}
let Component: NewReactJSXElementConstructor<{ title: string }>;
>Component : NewReactJSXElementConstructor<{ title: string; }>
>title : string
const RenderElement = ({ title }: { title: string }) => <div>{title}</div>;
>RenderElement : ({ title }: { title: string; }) => JSX.Element
>({ title }: { title: string }) => <div>{title}</div> : ({ title }: { title: string; }) => JSX.Element
>title : string
>title : string
><div>{title}</div> : JSX.Element
>div : any
>title : string
>div : any
Component = RenderElement;
>Component = RenderElement : ({ title }: { title: string; }) => JSX.Element
>Component : NewReactJSXElementConstructor<{ title: string; }>
>RenderElement : ({ title }: { title: string; }) => JSX.Element
<RenderElement />;
><RenderElement /> : JSX.Element
>RenderElement : ({ title }: { title: string; }) => JSX.Element
<RenderElement title="react" />;
><RenderElement title="react" /> : JSX.Element
>RenderElement : ({ title }: { title: string; }) => JSX.Element
>title : string
<RenderElement excessProp />;
><RenderElement excessProp /> : JSX.Element
>RenderElement : ({ title }: { title: string; }) => JSX.Element
>excessProp : true
const RenderString = ({ title }: { title: string }) => title;
>RenderString : ({ title }: { title: string; }) => string
>({ title }: { title: string }) => title : ({ title }: { title: string; }) => string
>title : string
>title : string
>title : string
Component = RenderString;
>Component = RenderString : ({ title }: { title: string; }) => string
>Component : NewReactJSXElementConstructor<{ title: string; }>
>RenderString : ({ title }: { title: string; }) => string
<RenderString />;
><RenderString /> : JSX.Element
>RenderString : ({ title }: { title: string; }) => string
<RenderString title="react" />;
><RenderString title="react" /> : JSX.Element
>RenderString : ({ title }: { title: string; }) => string
>title : string
<RenderString excessProp />;
><RenderString excessProp /> : JSX.Element
>RenderString : ({ title }: { title: string; }) => string
>excessProp : true
const RenderNumber = ({ title }: { title: string }) => title.length;
>RenderNumber : ({ title }: { title: string; }) => number
>({ title }: { title: string }) => title.length : ({ title }: { title: string; }) => number
>title : string
>title : string
>title.length : number
>title : string
>length : number
Component = RenderNumber;
>Component = RenderNumber : ({ title }: { title: string; }) => number
>Component : NewReactJSXElementConstructor<{ title: string; }>
>RenderNumber : ({ title }: { title: string; }) => number
<RenderNumber />;
><RenderNumber /> : JSX.Element
>RenderNumber : ({ title }: { title: string; }) => number
<RenderNumber title="react" />;
><RenderNumber title="react" /> : JSX.Element
>RenderNumber : ({ title }: { title: string; }) => number
>title : string
<RenderNumber excessProp />;
><RenderNumber excessProp /> : JSX.Element
>RenderNumber : ({ title }: { title: string; }) => number
>excessProp : true
const RenderArray = ({ title }: { title: string }) => [title];
>RenderArray : ({ title }: { title: string; }) => string[]
>({ title }: { title: string }) => [title] : ({ title }: { title: string; }) => string[]
>title : string
>title : string
>[title] : string[]
>title : string
Component = RenderArray;
>Component = RenderArray : ({ title }: { title: string; }) => string[]
>Component : NewReactJSXElementConstructor<{ title: string; }>
>RenderArray : ({ title }: { title: string; }) => string[]
<RenderArray />;
><RenderArray /> : JSX.Element
>RenderArray : ({ title }: { title: string; }) => string[]
<RenderArray title="react" />;
><RenderArray title="react" /> : JSX.Element
>RenderArray : ({ title }: { title: string; }) => string[]
>title : string
<RenderArray excessProp />;
><RenderArray excessProp /> : JSX.Element
>RenderArray : ({ title }: { title: string; }) => string[]
>excessProp : true
// React Server Component
const RenderPromise = async ({ title }: { title: string }) => "react";
>RenderPromise : ({ title }: { title: string; }) => Promise<string>
>async ({ title }: { title: string }) => "react" : ({ title }: { title: string; }) => Promise<string>
>title : string
>title : string
>"react" : "react"
Component = RenderPromise;
>Component = RenderPromise : ({ title }: { title: string; }) => Promise<string>
>Component : NewReactJSXElementConstructor<{ title: string; }>
>RenderPromise : ({ title }: { title: string; }) => Promise<string>
<RenderPromise />;
><RenderPromise /> : JSX.Element
>RenderPromise : ({ title }: { title: string; }) => Promise<string>
<RenderPromise title="react" />;
><RenderPromise title="react" /> : JSX.Element
>RenderPromise : ({ title }: { title: string; }) => Promise<string>
>title : string
<RenderPromise excessProp />;
><RenderPromise excessProp /> : JSX.Element
>RenderPromise : ({ title }: { title: string; }) => Promise<string>
>excessProp : true
// Class components still work
class RenderStringClass extends React.Component<{ title: string }> {
>RenderStringClass : RenderStringClass
>React.Component : React.Component<{ title: string; }, {}, any>
>React : typeof React
>Component : typeof React.Component
>title : string
render() {
>render : () => string
return this.props.title;
>this.props.title : string
>this.props : Readonly<{ children?: React.ReactNode; }> & Readonly<{ title: string; }>
>this : this
>props : Readonly<{ children?: React.ReactNode; }> & Readonly<{ title: string; }>
>title : string
}
}
Component = RenderStringClass;
>Component = RenderStringClass : typeof RenderStringClass
>Component : NewReactJSXElementConstructor<{ title: string; }>
>RenderStringClass : typeof RenderStringClass
<RenderStringClass />;
><RenderStringClass /> : JSX.Element
>RenderStringClass : typeof RenderStringClass
<RenderStringClass title="react" />;
><RenderStringClass title="react" /> : JSX.Element
>RenderStringClass : typeof RenderStringClass
>title : string
<RenderStringClass excessProp />;
><RenderStringClass excessProp /> : JSX.Element
>RenderStringClass : typeof RenderStringClass
>excessProp : true
// Host element types still work
<div />;
><div /> : JSX.Element
>div : any
<my-custom-element />;
><my-custom-element /> : JSX.Element
>my-custom-element : any
// Undeclared host element types are still rejected
<boop />;
><boop /> : JSX.Element
>boop : any
<my-undeclared-custom-element />;
><my-undeclared-custom-element /> : JSX.Element
>my-undeclared-custom-element : any
// Highlighting various ecosystem compat issues
// react-native-gesture-handler
// https://github.com/software-mansion/react-native-gesture-handler/blob/79017e5e7cc2e82e6467851f870920ff836ee04f/src/components/GestureComponents.tsx#L139-L146
interface ReactNativeFlatListProps<Item> {}
function ReactNativeFlatList(
>ReactNativeFlatList : (props: {}, ref: React.ForwardedRef<typeof ReactNativeFlatList>) => null
props: {},
>props : {}
ref: React.ForwardedRef<typeof ReactNativeFlatList>
>ref : React.ForwardedRef<(props: {}, ref: React.ForwardedRef<typeof ReactNativeFlatList>) => null>
>React : any
>ReactNativeFlatList : (props: {}, ref: React.ForwardedRef<typeof ReactNativeFlatList>) => null
) {
return null;
}
<ReactNativeFlatList />;
><ReactNativeFlatList /> : JSX.Element
>ReactNativeFlatList : (props: {}, ref: React.ForwardedRef<typeof ReactNativeFlatList>) => null
// testing higher-order component compat
function f1<T extends (props: {}) => React.ReactElement<any>>(Component: T) {
>f1 : <T extends (props: {}) => React.ReactElement<any>>(Component: T) => JSX.Element
>props : {}
>React : any
>Component : T
return <Component />;
><Component /> : JSX.Element
>Component : T
}
<Unresolved />;
><Unresolved /> : JSX.Element
>Unresolved : any
<Unresolved foo="abc" />;
><Unresolved foo="abc" /> : JSX.Element
>Unresolved : any
>foo : string

View File

@@ -0,0 +1,37 @@
tests/cases/compiler/jsxElementTypeLiteral.tsx(16,10): error TS2786: 'span' cannot be used as a JSX component.
Its type '"span"' is not a valid JSX element type.
tests/cases/compiler/jsxElementTypeLiteral.tsx(20,9): error TS2339: Property 'ruhroh' does not exist on type 'JSX.IntrinsicElements'.
tests/cases/compiler/jsxElementTypeLiteral.tsx(20,10): error TS2786: 'ruhroh' cannot be used as a JSX component.
Its type '"ruhroh"' is not a valid JSX element type.
==== tests/cases/compiler/jsxElementTypeLiteral.tsx (3 errors) ====
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
declare global {
namespace JSX {
// This should only use keys of JSX.IntrinsicElements.
// Diverging here to illustrate different error messages.
type ElementType = "div";
}
}
// should be fine - `ElementType` accepts `div`
let a = <div />;
// should be an error - `ElementType` does not accept `span`
let b = <span />;
~~~~
!!! error TS2786: 'span' cannot be used as a JSX component.
!!! error TS2786: Its type '"span"' is not a valid JSX element type.
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
~~~~~~~~~~
!!! error TS2339: Property 'ruhroh' does not exist on type 'JSX.IntrinsicElements'.
~~~~~~
!!! error TS2786: 'ruhroh' cannot be used as a JSX component.
!!! error TS2786: Its type '"ruhroh"' is not a valid JSX element type.

View File

@@ -0,0 +1,35 @@
//// [jsxElementTypeLiteral.tsx]
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
declare global {
namespace JSX {
// This should only use keys of JSX.IntrinsicElements.
// Diverging here to illustrate different error messages.
type ElementType = "div";
}
}
// should be fine - `ElementType` accepts `div`
let a = <div />;
// should be an error - `ElementType` does not accept `span`
let b = <span />;
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
//// [jsxElementTypeLiteral.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/// <reference path="react16.d.ts" />
var React = require("react");
// should be fine - `ElementType` accepts `div`
var a = React.createElement("div", null);
// should be an error - `ElementType` does not accept `span`
var b = React.createElement("span", null);
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
var c = React.createElement("ruhroh", null);

View File

@@ -0,0 +1,33 @@
=== tests/cases/compiler/jsxElementTypeLiteral.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : Symbol(React, Decl(jsxElementTypeLiteral.tsx, 1, 6))
declare global {
>global : Symbol(global, Decl(jsxElementTypeLiteral.tsx, 1, 31))
namespace JSX {
>JSX : Symbol(JSX, Decl(react16.d.ts, 2493, 12), Decl(jsxElementTypeLiteral.tsx, 3, 16))
// This should only use keys of JSX.IntrinsicElements.
// Diverging here to illustrate different error messages.
type ElementType = "div";
>ElementType : Symbol(ElementType, Decl(jsxElementTypeLiteral.tsx, 4, 17))
}
}
// should be fine - `ElementType` accepts `div`
let a = <div />;
>a : Symbol(a, Decl(jsxElementTypeLiteral.tsx, 12, 3))
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114))
// should be an error - `ElementType` does not accept `span`
let b = <span />;
>b : Symbol(b, Decl(jsxElementTypeLiteral.tsx, 15, 3))
>span : Symbol(JSX.IntrinsicElements.span, Decl(react16.d.ts, 2609, 114))
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
>c : Symbol(c, Decl(jsxElementTypeLiteral.tsx, 19, 3))

View File

@@ -0,0 +1,35 @@
=== tests/cases/compiler/jsxElementTypeLiteral.tsx ===
/// <reference path="react16.d.ts" />
import * as React from "react";
>React : typeof React
declare global {
>global : any
namespace JSX {
// This should only use keys of JSX.IntrinsicElements.
// Diverging here to illustrate different error messages.
type ElementType = "div";
>ElementType : "div"
}
}
// should be fine - `ElementType` accepts `div`
let a = <div />;
>a : JSX.Element
><div /> : JSX.Element
>div : any
// should be an error - `ElementType` does not accept `span`
let b = <span />;
>b : JSX.Element
><span /> : JSX.Element
>span : any
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;
>c : JSX.Element
><ruhroh /> : JSX.Element
>ruhroh : any

View File

@@ -0,0 +1,101 @@
// @strict: true
// @jsx: react
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
type React18ReactFragment = ReadonlyArray<React18ReactNode>;
type React18ReactNode =
| React.ReactElement<any>
| string
| number
| React18ReactFragment
| React.ReactPortal
| boolean
| null
| undefined
| Promise<React18ReactNode>;
// // React.JSXElementConstructor but it now can return React nodes from function components.
type NewReactJSXElementConstructor<P> =
| ((props: P) => React18ReactNode)
| (new (props: P) => React.Component<P, any>);
declare global {
namespace JSX {
type ElementType = string | NewReactJSXElementConstructor<any>;
interface IntrinsicElements {
['my-custom-element']: React.DOMAttributes<unknown>;
}
}
}
let Component: NewReactJSXElementConstructor<{ title: string }>;
const RenderElement = ({ title }: { title: string }) => <div>{title}</div>;
Component = RenderElement;
<RenderElement />;
<RenderElement title="react" />;
<RenderElement excessProp />;
const RenderString = ({ title }: { title: string }) => title;
Component = RenderString;
<RenderString />;
<RenderString title="react" />;
<RenderString excessProp />;
const RenderNumber = ({ title }: { title: string }) => title.length;
Component = RenderNumber;
<RenderNumber />;
<RenderNumber title="react" />;
<RenderNumber excessProp />;
const RenderArray = ({ title }: { title: string }) => [title];
Component = RenderArray;
<RenderArray />;
<RenderArray title="react" />;
<RenderArray excessProp />;
// React Server Component
const RenderPromise = async ({ title }: { title: string }) => "react";
Component = RenderPromise;
<RenderPromise />;
<RenderPromise title="react" />;
<RenderPromise excessProp />;
// Class components still work
class RenderStringClass extends React.Component<{ title: string }> {
render() {
return this.props.title;
}
}
Component = RenderStringClass;
<RenderStringClass />;
<RenderStringClass title="react" />;
<RenderStringClass excessProp />;
// Host element types still work
<div />;
<my-custom-element />;
// Undeclared host element types are still rejected
<boop />;
<my-undeclared-custom-element />;
// Highlighting various ecosystem compat issues
// react-native-gesture-handler
// https://github.com/software-mansion/react-native-gesture-handler/blob/79017e5e7cc2e82e6467851f870920ff836ee04f/src/components/GestureComponents.tsx#L139-L146
interface ReactNativeFlatListProps<Item> {}
function ReactNativeFlatList(
props: {},
ref: React.ForwardedRef<typeof ReactNativeFlatList>
) {
return null;
}
<ReactNativeFlatList />;
// testing higher-order component compat
function f1<T extends (props: {}) => React.ReactElement<any>>(Component: T) {
return <Component />;
}
<Unresolved />;
<Unresolved foo="abc" />;

View File

@@ -0,0 +1,22 @@
// @strict: true
// @jsx: react
/// <reference path="/.lib/react16.d.ts" />
import * as React from "react";
declare global {
namespace JSX {
// This should only use keys of JSX.IntrinsicElements.
// Diverging here to illustrate different error messages.
type ElementType = "div";
}
}
// should be fine - `ElementType` accepts `div`
let a = <div />;
// should be an error - `ElementType` does not accept `span`
let b = <span />;
// Should be an error.
// `ruhroh` is in neither `IntrinsicElements` nor `ElementType`
let c = <ruhroh />;