diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 480f7189fce31..2e9751bf24a1c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33791,7 +33791,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isJsxElement(parent) && parent.openingElement === openingLikeElement || isJsxFragment(parent) && parent.openingFragment === openingLikeElement) && getSemanticJsxChildren(parent.children).length > 0 ) { - const childrenTypes: Type[] = checkJsxChildren(parent, checkMode); + // Compute contextual type for fragment children before checking them + let childrenContextualType: Type | undefined; + if (isJsxOpeningFragment(openingLikeElement) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { + // For fragments, get the props type from the Fragment factory's signature + const fragmentType = getJSXFragmentType(openingLikeElement); + const signatures = getSignaturesOfType(fragmentType, SignatureKind.Call); + if (signatures.length > 0) { + const contextualType = getTypeOfFirstParameterOfSignature(signatures[0]); + childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); + } + } + + // Check children with contextual type (only for fragments) + const childrenTypes: Type[] = checkJsxChildren(parent, checkMode, childrenContextualType); if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { // Error if there is a attribute named "children" explicitly specified and children element. @@ -33842,7 +33855,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } - function checkJsxChildren(node: JsxElement | JsxFragment, checkMode?: CheckMode) { + function checkJsxChildren(node: JsxElement | JsxFragment, checkMode?: CheckMode, childrenContextualType?: Type) { const childrenTypes: Type[] = []; for (const child of node.children) { // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that @@ -33856,7 +33869,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { continue; // empty jsx expressions don't *really* count as present children } else { - childrenTypes.push(checkExpressionForMutableLocation(child, checkMode)); + // If we have a contextual type for children, use it when checking child expressions + if (childrenContextualType && child.kind === SyntaxKind.JsxExpression && child.expression) { + childrenTypes.push(checkExpressionForMutableLocationWithContextualType(child.expression, childrenContextualType)); + } + else { + childrenTypes.push(checkExpressionForMutableLocation(child, checkMode)); + } } } return childrenTypes; @@ -37418,15 +37437,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const jsxFragmentFactoryName = getJsxNamespace(node); // #38720/60122, allow null as jsxFragmentFactory - const shouldResolveFactoryReference = (compilerOptions.jsx === JsxEmit.React || compilerOptions.jsxFragmentFactory !== undefined) && jsxFragmentFactoryName !== "null"; + const shouldResolveFactoryReference = (compilerOptions.jsx === JsxEmit.React || compilerOptions.jsx === JsxEmit.ReactJSX || compilerOptions.jsx === JsxEmit.ReactJSXDev || compilerOptions.jsxFragmentFactory !== undefined) && jsxFragmentFactoryName !== "null"; if (!shouldResolveFactoryReference) return sourceFileLinks.jsxFragmentType = anyType; const shouldModuleRefErr = compilerOptions.jsx !== JsxEmit.Preserve && compilerOptions.jsx !== JsxEmit.ReactNative; + // When using react-jsx/react-jsxdev, the fragment comes from the jsx-runtime module as "Fragment" export + // Use "Fragment" in error message instead of the default "React" namespace + const isModernJsx = compilerOptions.jsx === JsxEmit.ReactJSX || compilerOptions.jsx === JsxEmit.ReactJSXDev; + const fragmentFactoryNameForError = isModernJsx ? ReactNames.Fragment : jsxFragmentFactoryName; const jsxFactoryRefErr = diagnostics ? Diagnostics.Using_JSX_fragments_requires_fragment_factory_0_to_be_in_scope_but_it_could_not_be_found : undefined; const jsxFactorySymbol = getJsxNamespaceContainerForImplicitImport(node) ?? resolveName( node, - jsxFragmentFactoryName, + fragmentFactoryNameForError, shouldModuleRefErr ? SymbolFlags.Value : SymbolFlags.Value & ~SymbolFlags.Enum, /*nameNotFoundMessage*/ jsxFactoryRefErr, /*isUse*/ true, diff --git a/tests/baselines/reference/jsxFragmentChildrenCheck.js b/tests/baselines/reference/jsxFragmentChildrenCheck.js new file mode 100644 index 0000000000000..5beaff1ef55e8 --- /dev/null +++ b/tests/baselines/reference/jsxFragmentChildrenCheck.js @@ -0,0 +1,57 @@ +//// [tests/cases/compiler/jsxFragmentChildrenCheck.tsx] //// + +//// [index.d.ts] +export const jsx: any; +export const jsxs: any; + +type JsxElement = + | JsxElementArray + | undefined + | string + | ((arg: { foo: "bar" }) => void); +interface JsxElementArray extends Array {} + +interface FragmentProps { + children?: JsxElement; +} + +export const Fragment: (props: FragmentProps) => any; + +declare global { + namespace JSX { + interface IntrinsicElements { + div: any; + span: any; + } + } +} + +//// [index.tsx] +import { Fragment } from "@test/jsx-runtime"; + +// This should pass - using explicit Fragment + + {"ok"} + {({ foo }) => "also ok"} +; + +// This should also pass - using <> syntax should be equivalent +<> + {"ok"} + {({ foo }) => "should also be ok"} +; + + +//// [index.js] +import { jsxs as _jsxs, Fragment as _Fragment } from "@test/jsx-runtime"; +import { Fragment } from "@test/jsx-runtime"; +// This should pass - using explicit Fragment +_jsxs(Fragment, { children: ["ok", function (_a) { + var foo = _a.foo; + return "also ok"; + }] }); +// This should also pass - using <> syntax should be equivalent +_jsxs(_Fragment, { children: ["ok", function (_a) { + var foo = _a.foo; + return "should also be ok"; + }] }); diff --git a/tests/baselines/reference/jsxFragmentChildrenCheck.symbols b/tests/baselines/reference/jsxFragmentChildrenCheck.symbols new file mode 100644 index 0000000000000..df5e6a1c145ae --- /dev/null +++ b/tests/baselines/reference/jsxFragmentChildrenCheck.symbols @@ -0,0 +1,80 @@ +//// [tests/cases/compiler/jsxFragmentChildrenCheck.tsx] //// + +=== node_modules/@test/jsx-runtime/index.d.ts === +export const jsx: any; +>jsx : Symbol(jsx, Decl(index.d.ts, 0, 12)) + +export const jsxs: any; +>jsxs : Symbol(jsxs, Decl(index.d.ts, 1, 12)) + +type JsxElement = +>JsxElement : Symbol(JsxElement, Decl(index.d.ts, 1, 23)) + + | JsxElementArray +>JsxElementArray : Symbol(JsxElementArray, Decl(index.d.ts, 7, 36)) + + | undefined + | string + | ((arg: { foo: "bar" }) => void); +>arg : Symbol(arg, Decl(index.d.ts, 7, 6)) +>foo : Symbol(foo, Decl(index.d.ts, 7, 12)) + +interface JsxElementArray extends Array {} +>JsxElementArray : Symbol(JsxElementArray, Decl(index.d.ts, 7, 36)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>JsxElement : Symbol(JsxElement, Decl(index.d.ts, 1, 23)) + +interface FragmentProps { +>FragmentProps : Symbol(FragmentProps, Decl(index.d.ts, 8, 54)) + + children?: JsxElement; +>children : Symbol(FragmentProps.children, Decl(index.d.ts, 10, 25)) +>JsxElement : Symbol(JsxElement, Decl(index.d.ts, 1, 23)) +} + +export const Fragment: (props: FragmentProps) => any; +>Fragment : Symbol(Fragment, Decl(index.d.ts, 14, 12)) +>props : Symbol(props, Decl(index.d.ts, 14, 24)) +>FragmentProps : Symbol(FragmentProps, Decl(index.d.ts, 8, 54)) + +declare global { +>global : Symbol(global, Decl(index.d.ts, 14, 53)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(index.d.ts, 16, 16)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(index.d.ts, 17, 17)) + + div: any; +>div : Symbol(IntrinsicElements.div, Decl(index.d.ts, 18, 33)) + + span: any; +>span : Symbol(IntrinsicElements.span, Decl(index.d.ts, 19, 15)) + } + } +} + +=== index.tsx === +import { Fragment } from "@test/jsx-runtime"; +>Fragment : Symbol(Fragment, Decl(index.tsx, 0, 8)) + +// This should pass - using explicit Fragment + +>Fragment : Symbol(Fragment, Decl(index.tsx, 0, 8)) + + {"ok"} + {({ foo }) => "also ok"} +>foo : Symbol(foo, Decl(index.tsx, 5, 5)) + +; +>Fragment : Symbol(Fragment, Decl(index.tsx, 0, 8)) + +// This should also pass - using <> syntax should be equivalent +<> + {"ok"} + {({ foo }) => "should also be ok"} +>foo : Symbol(foo, Decl(index.tsx, 11, 5)) + +; + diff --git a/tests/baselines/reference/jsxFragmentChildrenCheck.types b/tests/baselines/reference/jsxFragmentChildrenCheck.types new file mode 100644 index 0000000000000..baa622a81272f --- /dev/null +++ b/tests/baselines/reference/jsxFragmentChildrenCheck.types @@ -0,0 +1,96 @@ +//// [tests/cases/compiler/jsxFragmentChildrenCheck.tsx] //// + +=== node_modules/@test/jsx-runtime/index.d.ts === +export const jsx: any; +>jsx : any + +export const jsxs: any; +>jsxs : any + +type JsxElement = +>JsxElement : JsxElement +> : ^^^^^^^^^^ + + | JsxElementArray + | undefined + | string + | ((arg: { foo: "bar" }) => void); +>arg : { foo: "bar"; } +> : ^^^^^^^ ^^^ +>foo : "bar" +> : ^^^^^ + +interface JsxElementArray extends Array {} + +interface FragmentProps { + children?: JsxElement; +>children : JsxElement +> : ^^^^^^^^^^ +} + +export const Fragment: (props: FragmentProps) => any; +>Fragment : (props: FragmentProps) => any +> : ^ ^^ ^^^^^ +>props : FragmentProps +> : ^^^^^^^^^^^^^ + +declare global { +>global : any +> : ^^^ + + namespace JSX { + interface IntrinsicElements { + div: any; +>div : any + + span: any; +>span : any + } + } +} + +=== index.tsx === +import { Fragment } from "@test/jsx-runtime"; +>Fragment : (props: import("node_modules/@test/jsx-runtime/index").FragmentProps) => any +> : ^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ + +// This should pass - using explicit Fragment + +> {"ok"} {({ foo }) => "also ok"} : error +>Fragment : (props: import("node_modules/@test/jsx-runtime/index").FragmentProps) => any +> : ^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ + + {"ok"} +>"ok" : "ok" +> : ^^^^ + + {({ foo }) => "also ok"} +>({ foo }) => "also ok" : ({ foo }: { foo: "bar"; }) => string +> : ^ ^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : "bar" +> : ^^^^^ +>"also ok" : "also ok" +> : ^^^^^^^^^ + +; +>Fragment : (props: import("node_modules/@test/jsx-runtime/index").FragmentProps) => any +> : ^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ + +// This should also pass - using <> syntax should be equivalent +<> +><> {"ok"} {({ foo }) => "should also be ok"} : any + + {"ok"} +>"ok" : "ok" +> : ^^^^ + + {({ foo }) => "should also be ok"} +>({ foo }) => "should also be ok" : ({ foo }: { foo: "bar"; }) => string +> : ^ ^^^^^^^^^ ^^^^^^^^^^^^^^ +>foo : "bar" +> : ^^^^^ +>"should also be ok" : "should also be ok" +> : ^^^^^^^^^^^^^^^^^^^ + +; + diff --git a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt index 453fbd038e286..97b1dd60a777d 100644 --- a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt +++ b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt @@ -1,12 +1,15 @@ jsxFragmentFactoryReference.tsx(3,9): error TS2875: This JSX tag requires the module path 'react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. +jsxFragmentFactoryReference.tsx(3,9): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. -==== jsxFragmentFactoryReference.tsx (1 errors) ==== +==== jsxFragmentFactoryReference.tsx (2 errors) ==== export class LoggedOut { content = () => ( <> ~~ !!! error TS2875: This JSX tag requires the module path 'react/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. ) } \ No newline at end of file diff --git a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsxdev).errors.txt b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsxdev).errors.txt index 7529f71c4d566..400aa3bd175b7 100644 --- a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsxdev).errors.txt +++ b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsxdev).errors.txt @@ -1,12 +1,15 @@ jsxFragmentFactoryReference.tsx(3,9): error TS2875: This JSX tag requires the module path 'react/jsx-dev-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. +jsxFragmentFactoryReference.tsx(3,9): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. -==== jsxFragmentFactoryReference.tsx (1 errors) ==== +==== jsxFragmentFactoryReference.tsx (2 errors) ==== export class LoggedOut { content = () => ( <> ~~ !!! error TS2875: This JSX tag requires the module path 'react/jsx-dev-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. ) } \ No newline at end of file diff --git a/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsx).errors.txt index 248540ed384da..2b1ec10df02e7 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsx).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsx).errors.txt @@ -1,11 +1,14 @@ jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2875: This JSX tag requires the module path 'preact/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. +jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. -==== jsxJsxsCjsTransformCustomImport.tsx (1 errors) ==== +==== jsxJsxsCjsTransformCustomImport.tsx (2 errors) ==== /// const a = <> ~~ !!! error TS2875: This JSX tag requires the module path 'preact/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found.

text
diff --git a/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsxdev).errors.txt b/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsxdev).errors.txt index e66480f8dce2f..062df44ff8029 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsxdev).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsxdev).errors.txt @@ -1,11 +1,14 @@ jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2875: This JSX tag requires the module path 'preact/jsx-dev-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. +jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. -==== jsxJsxsCjsTransformCustomImport.tsx (1 errors) ==== +==== jsxJsxsCjsTransformCustomImport.tsx (2 errors) ==== /// const a = <> ~~ !!! error TS2875: This JSX tag requires the module path 'preact/jsx-dev-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found.

text
diff --git a/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsx).errors.txt index c37e010cab834..61276f782e430 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsx).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsx).errors.txt @@ -1,4 +1,5 @@ preact.tsx(3,11): error TS2875: This JSX tag requires the module path 'preact/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. +preact.tsx(3,11): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. ==== react.tsx (0 errors) ==== @@ -12,12 +13,14 @@ preact.tsx(3,11): error TS2875: This JSX tag requires the module path 'preact/js export {}; -==== preact.tsx (1 errors) ==== +==== preact.tsx (2 errors) ==== /// /* @jsxImportSource preact */ const a = <> ~~ !!! error TS2875: This JSX tag requires the module path 'preact/jsx-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found.

text
diff --git a/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsxdev).errors.txt b/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsxdev).errors.txt index 79e8e02a87b2f..3a1dfd23eb335 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsxdev).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsxdev).errors.txt @@ -1,4 +1,5 @@ preact.tsx(3,11): error TS2875: This JSX tag requires the module path 'preact/jsx-dev-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. +preact.tsx(3,11): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. ==== react.tsx (0 errors) ==== @@ -12,12 +13,14 @@ preact.tsx(3,11): error TS2875: This JSX tag requires the module path 'preact/js export {}; -==== preact.tsx (1 errors) ==== +==== preact.tsx (2 errors) ==== /// /* @jsxImportSource preact */ const a = <> ~~ !!! error TS2875: This JSX tag requires the module path 'preact/jsx-dev-runtime' to exist, but none could be found. Make sure you have types for the appropriate package installed. + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found.

text
diff --git a/tests/baselines/reference/jsxRuntimePragma(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxRuntimePragma(jsx=react-jsx).errors.txt new file mode 100644 index 0000000000000..988e3e9d679ea --- /dev/null +++ b/tests/baselines/reference/jsxRuntimePragma(jsx=react-jsx).errors.txt @@ -0,0 +1,41 @@ +four.tsx(6,21): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. +one.tsx(5,21): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. + + +==== one.tsx (1 errors) ==== + /// + /* @jsxRuntime classic */ + import * as React from "react"; + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. + export const selfClosing = ; +==== two.tsx (0 errors) ==== + /// + /* @jsxRuntime automatic */ + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + export const selfClosing = ; +==== three.tsx (0 errors) ==== + /// + /* @jsxRuntime classic */ + /* @jsxRuntime automatic */ + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + export const selfClosing = ; +==== four.tsx (1 errors) ==== + /// + /* @jsxRuntime automatic */ + /* @jsxRuntime classic */ + import * as React from "react"; + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. + export const selfClosing = ; +==== index.ts (0 errors) ==== + export * as one from "./one.js"; + export * as two from "./two.js"; + export * as three from "./three.js"; + export * as four from "./four.js"; \ No newline at end of file diff --git a/tests/baselines/reference/jsxRuntimePragma(jsx=react-jsxdev).errors.txt b/tests/baselines/reference/jsxRuntimePragma(jsx=react-jsxdev).errors.txt new file mode 100644 index 0000000000000..988e3e9d679ea --- /dev/null +++ b/tests/baselines/reference/jsxRuntimePragma(jsx=react-jsxdev).errors.txt @@ -0,0 +1,41 @@ +four.tsx(6,21): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. +one.tsx(5,21): error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. + + +==== one.tsx (1 errors) ==== + /// + /* @jsxRuntime classic */ + import * as React from "react"; + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. + export const selfClosing = ; +==== two.tsx (0 errors) ==== + /// + /* @jsxRuntime automatic */ + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + export const selfClosing = ; +==== three.tsx (0 errors) ==== + /// + /* @jsxRuntime classic */ + /* @jsxRuntime automatic */ + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + export const selfClosing = ; +==== four.tsx (1 errors) ==== + /// + /* @jsxRuntime automatic */ + /* @jsxRuntime classic */ + import * as React from "react"; + export const HelloWorld = () =>

Hello world

; + export const frag = <>
; + ~~ +!!! error TS2879: Using JSX fragments requires fragment factory 'Fragment' to be in scope, but it could not be found. + export const selfClosing = ; +==== index.ts (0 errors) ==== + export * as one from "./one.js"; + export * as two from "./two.js"; + export * as three from "./three.js"; + export * as four from "./four.js"; \ No newline at end of file diff --git a/tests/cases/compiler/jsxFragmentChildrenCheck.tsx b/tests/cases/compiler/jsxFragmentChildrenCheck.tsx new file mode 100644 index 0000000000000..d290747645655 --- /dev/null +++ b/tests/cases/compiler/jsxFragmentChildrenCheck.tsx @@ -0,0 +1,46 @@ +// @jsx: react-jsx +// @strict: true +// @module: esnext +// @skipLibCheck: true +// @jsxImportSource: @test + +// @filename: node_modules/@test/jsx-runtime/index.d.ts +export const jsx: any; +export const jsxs: any; + +type JsxElement = + | JsxElementArray + | undefined + | string + | ((arg: { foo: "bar" }) => void); +interface JsxElementArray extends Array {} + +interface FragmentProps { + children?: JsxElement; +} + +export const Fragment: (props: FragmentProps) => any; + +declare global { + namespace JSX { + interface IntrinsicElements { + div: any; + span: any; + } + } +} + +// @filename: index.tsx +import { Fragment } from "@test/jsx-runtime"; + +// This should pass - using explicit Fragment + + {"ok"} + {({ foo }) => "also ok"} +; + +// This should also pass - using <> syntax should be equivalent +<> + {"ok"} + {({ foo }) => "should also be ok"} +;