diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e889a93ef2394..dafa64f066d7f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -30328,7 +30328,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); } @@ -30793,6 +30793,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 */ @@ -30861,7 +30867,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); + } } } @@ -48800,6 +48820,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; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 804ffeeff8ffa..7816d4ec7976e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -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 } } diff --git a/tests/baselines/reference/jsxElementType.errors.txt b/tests/baselines/reference/jsxElementType.errors.txt new file mode 100644 index 0000000000000..ddd2efe19cce7 --- /dev/null +++ b/tests/baselines/reference/jsxElementType.errors.txt @@ -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 & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'. + Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & 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 & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'. + Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & 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) => null' is not a valid JSX element type. + Type '(props: {}, ref: ForwardedRef) => 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'. +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) ==== + /// + import * as React from "react"; + + type React18ReactFragment = ReadonlyArray; + type React18ReactNode = + | React.ReactElement + | string + | number + | React18ReactFragment + | React.ReactPortal + | boolean + | null + | undefined + | Promise; + + // // React.JSXElementConstructor but it now can return React nodes from function components. + type NewReactJSXElementConstructor

= + | ((props: P) => React18ReactNode) + | (new (props: P) => React.Component); + + declare global { + namespace JSX { + type ElementType = string | NewReactJSXElementConstructor; + interface IntrinsicElements { + ['my-custom-element']: React.DOMAttributes; + } + } + } + + let Component: NewReactJSXElementConstructor<{ title: string }>; + + const RenderElement = ({ title }: { title: string }) =>

{title}
; + Component = 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. + ; + ; + ~~~~~~~~~~ +!!! 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; + ; + ~~~~~~~~~~~~ +!!! 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. + ; + ; + ~~~~~~~~~~ +!!! 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; + ; + ~~~~~~~~~~~~ +!!! 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. + ; + ; + ~~~~~~~~~~ +!!! 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; + ; + ~~~~~~~~~~~ +!!! 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. + ; + ; + ~~~~~~~~~~ +!!! 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; + ; + ~~~~~~~~~~~~~ +!!! 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. + ; + ; + ~~~~~~~~~~ +!!! 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; + ; + ~~~~~~~~~~~~~~~~~ +!!! 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. + ; + ; + ~~~~~~~~~~ +!!! 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 & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'. +!!! error TS2769: Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & 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 & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'. +!!! error TS2769: Property 'excessProp' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ title: string; }>'. + + // Host element types still work +
; + ; + // Undeclared host element types are still rejected + ; + ~~~~~~~~ +!!! error TS2339: Property 'boop' does not exist on type 'JSX.IntrinsicElements'. + ; + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! 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 {} + function ReactNativeFlatList( + props: {}, + ref: React.ForwardedRef + ) { + return null; + } + ; + ~~~~~~~~~~~~~~~~~~~ +!!! error TS2786: 'ReactNativeFlatList' cannot be used as a JSX component. +!!! error TS2786: Its type '(props: {}, ref: ForwardedRef) => null' is not a valid JSX element type. +!!! error TS2786: Type '(props: {}, ref: ForwardedRef) => 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 React.ReactElement>(Component: T) { + return ; + ~~~~~~~~~ +!!! error TS2322: Type '{}' is not assignable to type 'LibraryManagedAttributes'. + } + + ; + ~~~~~~~~~~ +!!! error TS2304: Cannot find name 'Unresolved'. + ; + ~~~~~~~~~~ +!!! error TS2304: Cannot find name 'Unresolved'. + \ No newline at end of file diff --git a/tests/baselines/reference/jsxElementType.js b/tests/baselines/reference/jsxElementType.js new file mode 100644 index 0000000000000..13986b0c62e90 --- /dev/null +++ b/tests/baselines/reference/jsxElementType.js @@ -0,0 +1,233 @@ +//// [jsxElementType.tsx] +/// +import * as React from "react"; + +type React18ReactFragment = ReadonlyArray; +type React18ReactNode = + | React.ReactElement + | string + | number + | React18ReactFragment + | React.ReactPortal + | boolean + | null + | undefined + | Promise; + +// // React.JSXElementConstructor but it now can return React nodes from function components. +type NewReactJSXElementConstructor

= + | ((props: P) => React18ReactNode) + | (new (props: P) => React.Component); + +declare global { + namespace JSX { + type ElementType = string | NewReactJSXElementConstructor; + interface IntrinsicElements { + ['my-custom-element']: React.DOMAttributes; + } + } +} + +let Component: NewReactJSXElementConstructor<{ title: string }>; + +const RenderElement = ({ title }: { title: string }) =>

{title}
; +Component = RenderElement; +; +; +; + +const RenderString = ({ title }: { title: string }) => title; +Component = RenderString; +; +; +; + +const RenderNumber = ({ title }: { title: string }) => title.length; +Component = RenderNumber; +; +; +; + +const RenderArray = ({ title }: { title: string }) => [title]; +Component = RenderArray; +; +; +; + +// React Server Component +const RenderPromise = async ({ title }: { title: string }) => "react"; +Component = RenderPromise; +; +; +; + +// Class components still work +class RenderStringClass extends React.Component<{ title: string }> { + render() { + return this.props.title; + } +} +Component = RenderStringClass; +; +; +; + +// Host element types still work +
; +; +// Undeclared host element types are still rejected +; +; + +// 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 {} +function ReactNativeFlatList( + props: {}, + ref: React.ForwardedRef +) { + return null; +} +; + +// testing higher-order component compat +function f1 React.ReactElement>(Component: T) { + return ; +} + +; +; + + +//// [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 }); +/// +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" }); diff --git a/tests/baselines/reference/jsxElementType.symbols b/tests/baselines/reference/jsxElementType.symbols new file mode 100644 index 0000000000000..c932009d84a1f --- /dev/null +++ b/tests/baselines/reference/jsxElementType.symbols @@ -0,0 +1,274 @@ +=== tests/cases/compiler/jsxElementType.tsx === +/// +import * as React from "react"; +>React : Symbol(React, Decl(jsxElementType.tsx, 1, 6)) + +type React18ReactFragment = ReadonlyArray; +>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 +>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; +>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

= +>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); +>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; +>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; +>['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 }) =>

{title}
; +>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 : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5)) + +; +>RenderElement : Symbol(RenderElement, Decl(jsxElementType.tsx, 31, 5)) +>title : Symbol(title, Decl(jsxElementType.tsx, 34, 14)) + +; +>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 : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5)) + +; +>RenderString : Symbol(RenderString, Decl(jsxElementType.tsx, 37, 5)) +>title : Symbol(title, Decl(jsxElementType.tsx, 40, 13)) + +; +>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 : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5)) + +; +>RenderNumber : Symbol(RenderNumber, Decl(jsxElementType.tsx, 43, 5)) +>title : Symbol(title, Decl(jsxElementType.tsx, 46, 13)) + +; +>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 : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5)) + +; +>RenderArray : Symbol(RenderArray, Decl(jsxElementType.tsx, 49, 5)) +>title : Symbol(title, Decl(jsxElementType.tsx, 52, 12)) + +; +>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 : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5)) + +; +>RenderPromise : Symbol(RenderPromise, Decl(jsxElementType.tsx, 56, 5)) +>title : Symbol(title, Decl(jsxElementType.tsx, 59, 14)) + +; +>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 : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29)) + +; +>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29)) +>title : Symbol(title, Decl(jsxElementType.tsx, 70, 18)) + +; +>RenderStringClass : Symbol(RenderStringClass, Decl(jsxElementType.tsx, 60, 29)) +>excessProp : Symbol(excessProp, Decl(jsxElementType.tsx, 71, 18)) + +// Host element types still work +
; +>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2546, 114)) + +; +>my-custom-element : Symbol(JSX.IntrinsicElements['my-custom-element'], Decl(jsxElementType.tsx, 23, 33)) + +// Undeclared host element types are still rejected +; +; + +// 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 {} +>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 +>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 : Symbol(ReactNativeFlatList, Decl(jsxElementType.tsx, 83, 43)) + +// testing higher-order component compat +function f1 React.ReactElement>(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 : Symbol(Component, Decl(jsxElementType.tsx, 93, 62)) +} + +; +; +>foo : Symbol(foo, Decl(jsxElementType.tsx, 98, 11)) + diff --git a/tests/baselines/reference/jsxElementType.types b/tests/baselines/reference/jsxElementType.types new file mode 100644 index 0000000000000..4e9a5a9a8ccb6 --- /dev/null +++ b/tests/baselines/reference/jsxElementType.types @@ -0,0 +1,292 @@ +=== tests/cases/compiler/jsxElementType.tsx === +/// +import * as React from "react"; +>React : typeof React + +type React18ReactFragment = ReadonlyArray; +>React18ReactFragment : readonly React18ReactNode[] + +type React18ReactNode = +>React18ReactNode : string | number | boolean | React.ReactElement | React.ReactPortal | React18ReactFragment | Promise | null | undefined + + | React.ReactElement +>React : any + + | string + | number + | React18ReactFragment + | React.ReactPortal +>React : any + + | boolean + | null + | undefined + | Promise; + +// // React.JSXElementConstructor but it now can return React nodes from function components. +type NewReactJSXElementConstructor

= +>NewReactJSXElementConstructor : NewReactJSXElementConstructor

+ + | ((props: P) => React18ReactNode) +>props : P + + | (new (props: P) => React.Component); +>props : P +>React : any + +declare global { +>global : any + + namespace JSX { + type ElementType = string | NewReactJSXElementConstructor; +>ElementType : string | NewReactJSXElementConstructor + + interface IntrinsicElements { + ['my-custom-element']: React.DOMAttributes; +>['my-custom-element'] : React.DOMAttributes +>'my-custom-element' : "my-custom-element" +>React : any + } + } +} + +let Component: NewReactJSXElementConstructor<{ title: string }>; +>Component : NewReactJSXElementConstructor<{ title: string; }> +>title : string + +const RenderElement = ({ title }: { title: string }) =>

{title}
; +>RenderElement : ({ title }: { title: string; }) => JSX.Element +>({ title }: { title: string }) =>
{title}
: ({ title }: { title: string; }) => JSX.Element +>title : string +>title : string +>
{title}
: 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 + +; +> : JSX.Element +>RenderElement : ({ title }: { title: string; }) => JSX.Element + +; +> : JSX.Element +>RenderElement : ({ title }: { title: string; }) => JSX.Element +>title : string + +; +> : 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 + +; +> : JSX.Element +>RenderString : ({ title }: { title: string; }) => string + +; +> : JSX.Element +>RenderString : ({ title }: { title: string; }) => string +>title : string + +; +> : 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 + +; +> : JSX.Element +>RenderNumber : ({ title }: { title: string; }) => number + +; +> : JSX.Element +>RenderNumber : ({ title }: { title: string; }) => number +>title : string + +; +> : 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[] + +; +> : JSX.Element +>RenderArray : ({ title }: { title: string; }) => string[] + +; +> : JSX.Element +>RenderArray : ({ title }: { title: string; }) => string[] +>title : string + +; +> : JSX.Element +>RenderArray : ({ title }: { title: string; }) => string[] +>excessProp : true + +// React Server Component +const RenderPromise = async ({ title }: { title: string }) => "react"; +>RenderPromise : ({ title }: { title: string; }) => Promise +>async ({ title }: { title: string }) => "react" : ({ title }: { title: string; }) => Promise +>title : string +>title : string +>"react" : "react" + +Component = RenderPromise; +>Component = RenderPromise : ({ title }: { title: string; }) => Promise +>Component : NewReactJSXElementConstructor<{ title: string; }> +>RenderPromise : ({ title }: { title: string; }) => Promise + +; +> : JSX.Element +>RenderPromise : ({ title }: { title: string; }) => Promise + +; +> : JSX.Element +>RenderPromise : ({ title }: { title: string; }) => Promise +>title : string + +; +> : JSX.Element +>RenderPromise : ({ title }: { title: string; }) => Promise +>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 + +; +> : JSX.Element +>RenderStringClass : typeof RenderStringClass + +; +> : JSX.Element +>RenderStringClass : typeof RenderStringClass +>title : string + +; +> : JSX.Element +>RenderStringClass : typeof RenderStringClass +>excessProp : true + +// Host element types still work +
; +>
: JSX.Element +>div : any + +; +> : JSX.Element +>my-custom-element : any + +// Undeclared host element types are still rejected +; +> : JSX.Element +>boop : any + +; +> : 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 {} +function ReactNativeFlatList( +>ReactNativeFlatList : (props: {}, ref: React.ForwardedRef) => null + + props: {}, +>props : {} + + ref: React.ForwardedRef +>ref : React.ForwardedRef<(props: {}, ref: React.ForwardedRef) => null> +>React : any +>ReactNativeFlatList : (props: {}, ref: React.ForwardedRef) => null + +) { + return null; +} +; +> : JSX.Element +>ReactNativeFlatList : (props: {}, ref: React.ForwardedRef) => null + +// testing higher-order component compat +function f1 React.ReactElement>(Component: T) { +>f1 : React.ReactElement>(Component: T) => JSX.Element +>props : {} +>React : any +>Component : T + + return ; +> : JSX.Element +>Component : T +} + +; +> : JSX.Element +>Unresolved : any + +; +> : JSX.Element +>Unresolved : any +>foo : string + diff --git a/tests/baselines/reference/jsxElementTypeLiteral.errors.txt b/tests/baselines/reference/jsxElementTypeLiteral.errors.txt new file mode 100644 index 0000000000000..942df5ad7c5a9 --- /dev/null +++ b/tests/baselines/reference/jsxElementTypeLiteral.errors.txt @@ -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) ==== + /// + 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 =
; + + // should be an error - `ElementType` does not accept `span` + let b = ; + ~~~~ +!!! 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 = ; + ~~~~~~~~~~ +!!! 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. + \ No newline at end of file diff --git a/tests/baselines/reference/jsxElementTypeLiteral.js b/tests/baselines/reference/jsxElementTypeLiteral.js new file mode 100644 index 0000000000000..95472a281f0e1 --- /dev/null +++ b/tests/baselines/reference/jsxElementTypeLiteral.js @@ -0,0 +1,35 @@ +//// [jsxElementTypeLiteral.tsx] +/// +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 =
; + +// should be an error - `ElementType` does not accept `span` +let b = ; + +// Should be an error. +// `ruhroh` is in neither `IntrinsicElements` nor `ElementType` +let c = ; + + +//// [jsxElementTypeLiteral.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +/// +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); diff --git a/tests/baselines/reference/jsxElementTypeLiteral.symbols b/tests/baselines/reference/jsxElementTypeLiteral.symbols new file mode 100644 index 0000000000000..ffd61d8a345b1 --- /dev/null +++ b/tests/baselines/reference/jsxElementTypeLiteral.symbols @@ -0,0 +1,33 @@ +=== tests/cases/compiler/jsxElementTypeLiteral.tsx === +/// +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 =
; +>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 = ; +>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 = ; +>c : Symbol(c, Decl(jsxElementTypeLiteral.tsx, 19, 3)) + diff --git a/tests/baselines/reference/jsxElementTypeLiteral.types b/tests/baselines/reference/jsxElementTypeLiteral.types new file mode 100644 index 0000000000000..998c4e8dbeb6c --- /dev/null +++ b/tests/baselines/reference/jsxElementTypeLiteral.types @@ -0,0 +1,35 @@ +=== tests/cases/compiler/jsxElementTypeLiteral.tsx === +/// +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 =
; +>a : JSX.Element +>
: JSX.Element +>div : any + +// should be an error - `ElementType` does not accept `span` +let b = ; +>b : JSX.Element +> : JSX.Element +>span : any + +// Should be an error. +// `ruhroh` is in neither `IntrinsicElements` nor `ElementType` +let c = ; +>c : JSX.Element +> : JSX.Element +>ruhroh : any + diff --git a/tests/cases/compiler/jsxElementType.tsx b/tests/cases/compiler/jsxElementType.tsx new file mode 100644 index 0000000000000..75585a2d99a43 --- /dev/null +++ b/tests/cases/compiler/jsxElementType.tsx @@ -0,0 +1,101 @@ +// @strict: true +// @jsx: react +/// +import * as React from "react"; + +type React18ReactFragment = ReadonlyArray; +type React18ReactNode = + | React.ReactElement + | string + | number + | React18ReactFragment + | React.ReactPortal + | boolean + | null + | undefined + | Promise; + +// // React.JSXElementConstructor but it now can return React nodes from function components. +type NewReactJSXElementConstructor

= + | ((props: P) => React18ReactNode) + | (new (props: P) => React.Component); + +declare global { + namespace JSX { + type ElementType = string | NewReactJSXElementConstructor; + interface IntrinsicElements { + ['my-custom-element']: React.DOMAttributes; + } + } +} + +let Component: NewReactJSXElementConstructor<{ title: string }>; + +const RenderElement = ({ title }: { title: string }) =>

{title}
; +Component = RenderElement; +; +; +; + +const RenderString = ({ title }: { title: string }) => title; +Component = RenderString; +; +; +; + +const RenderNumber = ({ title }: { title: string }) => title.length; +Component = RenderNumber; +; +; +; + +const RenderArray = ({ title }: { title: string }) => [title]; +Component = RenderArray; +; +; +; + +// React Server Component +const RenderPromise = async ({ title }: { title: string }) => "react"; +Component = RenderPromise; +; +; +; + +// Class components still work +class RenderStringClass extends React.Component<{ title: string }> { + render() { + return this.props.title; + } +} +Component = RenderStringClass; +; +; +; + +// Host element types still work +
; +; +// Undeclared host element types are still rejected +; +; + +// 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 {} +function ReactNativeFlatList( + props: {}, + ref: React.ForwardedRef +) { + return null; +} +; + +// testing higher-order component compat +function f1 React.ReactElement>(Component: T) { + return ; +} + +; +; diff --git a/tests/cases/compiler/jsxElementTypeLiteral.tsx b/tests/cases/compiler/jsxElementTypeLiteral.tsx new file mode 100644 index 0000000000000..79c3b0e15767e --- /dev/null +++ b/tests/cases/compiler/jsxElementTypeLiteral.tsx @@ -0,0 +1,22 @@ +// @strict: true +// @jsx: react +/// +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 =
; + +// should be an error - `ElementType` does not accept `span` +let b = ; + +// Should be an error. +// `ruhroh` is in neither `IntrinsicElements` nor `ElementType` +let c = ;