From 607b94997019b175f200c983041e041f5ccfc65d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 15:56:06 +0000 Subject: [PATCH 1/4] Initial plan From 483093f36f05fd02488e1cc92dfe56f9041fb690 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:12:35 +0000 Subject: [PATCH 2/4] Add test case and implement fix for JSX Fragment typing with react-jsx mode Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- src/compiler/checker.ts | 36 +++++-- .../reference/jsxFragmentChildrenCheck.js | 57 +++++++++++ .../jsxFragmentChildrenCheck.symbols | 80 ++++++++++++++++ .../reference/jsxFragmentChildrenCheck.types | 96 +++++++++++++++++++ .../compiler/jsxFragmentChildrenCheck.tsx | 46 +++++++++ 5 files changed, 308 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/jsxFragmentChildrenCheck.js create mode 100644 tests/baselines/reference/jsxFragmentChildrenCheck.symbols create mode 100644 tests/baselines/reference/jsxFragmentChildrenCheck.types create mode 100644 tests/cases/compiler/jsxFragmentChildrenCheck.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 480f7189fce31..9bafc0c98385e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33791,7 +33791,26 @@ 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 children before checking them + let childrenContextualType: Type | undefined; + if (jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { + let contextualType: Type | undefined; + if (isJsxOpeningElement(openingLikeElement)) { + contextualType = getApparentTypeOfContextualType(openingLikeElement.attributes, /*contextFlags*/ undefined); + } + else if (isJsxOpeningFragment(openingLikeElement)) { + // 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) { + contextualType = getTypeOfFirstParameterOfSignature(signatures[0]); + } + } + childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); + } + + // Check children with contextual type + const childrenTypes: Type[] = checkJsxChildren(parent, checkMode, childrenContextualType); if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { // Error if there is a attribute named "children" explicitly specified and children element. @@ -33800,9 +33819,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (explicitlySpecifyChildrenAttribute) { error(attributeParent, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, unescapeLeadingUnderscores(jsxChildrenPropertyName)); } - - const contextualType = isJsxOpeningElement(openingLikeElement) ? getApparentTypeOfContextualType(openingLikeElement.attributes, /*contextFlags*/ undefined) : undefined; - const childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process const childrenPropSymbol = createSymbol(SymbolFlags.Property, jsxChildrenPropertyName); childrenPropSymbol.links.type = childrenTypes.length === 1 ? childrenTypes[0] : @@ -33842,7 +33858,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 +33872,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,7 +37440,7 @@ 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; 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/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"} +; From 7fcfd01ec36f8fd209432e139a50d1c3e9469fca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:20:01 +0000 Subject: [PATCH 3/4] Fix regression: only apply contextual type for fragment children, not regular JSX elements Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- src/compiler/checker.ts | 27 +++++++++---------- ...FactoryReference(jsx=react-jsx).errors.txt | 5 +++- ...toryReference(jsx=react-jsxdev).errors.txt | 5 +++- ...formCustomImport(jsx=react-jsx).errors.txt | 5 +++- ...mCustomImport(jsx=react-jsxdev).errors.txt | 5 +++- ...stomImportPragma(jsx=react-jsx).errors.txt | 5 +++- ...mImportPragma(jsx=react-jsxdev).errors.txt | 5 +++- 7 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9bafc0c98385e..f3cf9c10ff3cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -33791,25 +33791,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { (isJsxElement(parent) && parent.openingElement === openingLikeElement || isJsxFragment(parent) && parent.openingFragment === openingLikeElement) && getSemanticJsxChildren(parent.children).length > 0 ) { - // Compute contextual type for children before checking them + // Compute contextual type for fragment children before checking them let childrenContextualType: Type | undefined; - if (jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { - let contextualType: Type | undefined; - if (isJsxOpeningElement(openingLikeElement)) { - contextualType = getApparentTypeOfContextualType(openingLikeElement.attributes, /*contextFlags*/ 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); } - else if (isJsxOpeningFragment(openingLikeElement)) { - // 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) { - contextualType = getTypeOfFirstParameterOfSignature(signatures[0]); - } - } - childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); } - // Check children with contextual type + // Check children with contextual type (only for fragments) const childrenTypes: Type[] = checkJsxChildren(parent, checkMode, childrenContextualType); if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") { @@ -33819,6 +33813,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (explicitlySpecifyChildrenAttribute) { error(attributeParent, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, unescapeLeadingUnderscores(jsxChildrenPropertyName)); } + + const contextualType = isJsxOpeningElement(openingLikeElement) ? getApparentTypeOfContextualType(openingLikeElement.attributes, /*contextFlags*/ undefined) : undefined; + const childrenContextualType = contextualType && getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName); // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process const childrenPropSymbol = createSymbol(SymbolFlags.Property, jsxChildrenPropertyName); childrenPropSymbol.links.type = childrenTypes.length === 1 ? childrenTypes[0] : diff --git a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt index 453fbd038e286..c8caaefa362d0 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 'React' 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 'React' 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..16bb6f5a54423 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 'React' 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 'React' 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..2b8fbaf4e46f0 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 'React' 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 'React' 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..31af5093deb3a 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 'React' 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 'React' 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..7155c43a5dad4 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 'React' 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 'React' 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..ad4bb3c20b12a 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 'React' 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 'React' to be in scope, but it could not be found.

text
From 983250fefc38dcc7cab7a22905bde138820c6029 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:49:56 +0000 Subject: [PATCH 4/4] Fix error message to show 'Fragment' instead of 'React' for react-jsx modes Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com> --- src/compiler/checker.ts | 6 ++- ...FactoryReference(jsx=react-jsx).errors.txt | 4 +- ...toryReference(jsx=react-jsxdev).errors.txt | 4 +- ...formCustomImport(jsx=react-jsx).errors.txt | 4 +- ...mCustomImport(jsx=react-jsxdev).errors.txt | 4 +- ...stomImportPragma(jsx=react-jsx).errors.txt | 4 +- ...mImportPragma(jsx=react-jsxdev).errors.txt | 4 +- ...jsxRuntimePragma(jsx=react-jsx).errors.txt | 41 +++++++++++++++++++ ...RuntimePragma(jsx=react-jsxdev).errors.txt | 41 +++++++++++++++++++ 9 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/jsxRuntimePragma(jsx=react-jsx).errors.txt create mode 100644 tests/baselines/reference/jsxRuntimePragma(jsx=react-jsxdev).errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f3cf9c10ff3cf..2e9751bf24a1c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -37441,11 +37441,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { 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/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt index c8caaefa362d0..97b1dd60a777d 100644 --- a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt +++ b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsx).errors.txt @@ -1,5 +1,5 @@ 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 'React' to be in scope, but it could not be found. +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 (2 errors) ==== @@ -9,7 +9,7 @@ jsxFragmentFactoryReference.tsx(3,9): error TS2879: Using JSX fragments requires ~~ !!! 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 'React' to be in scope, but it could not be found. +!!! 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 16bb6f5a54423..400aa3bd175b7 100644 --- a/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsxdev).errors.txt +++ b/tests/baselines/reference/jsxFragmentFactoryReference(jsx=react-jsxdev).errors.txt @@ -1,5 +1,5 @@ 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 'React' to be in scope, but it could not be found. +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 (2 errors) ==== @@ -9,7 +9,7 @@ jsxFragmentFactoryReference.tsx(3,9): error TS2879: Using JSX fragments requires ~~ !!! 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 'React' to be in scope, but it could not be found. +!!! 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 2b8fbaf4e46f0..2b1ec10df02e7 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsx).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsx).errors.txt @@ -1,5 +1,5 @@ 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 'React' to be in scope, but it could not be found. +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 (2 errors) ==== @@ -8,7 +8,7 @@ jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2879: Using JSX fragments req ~~ !!! 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 'React' to be in scope, but it could not be found. +!!! 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 31af5093deb3a..062df44ff8029 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsxdev).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImport(jsx=react-jsxdev).errors.txt @@ -1,5 +1,5 @@ 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 'React' to be in scope, but it could not be found. +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 (2 errors) ==== @@ -8,7 +8,7 @@ jsxJsxsCjsTransformCustomImport.tsx(2,11): error TS2879: Using JSX fragments req ~~ !!! 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 'React' to be in scope, but it could not be found. +!!! 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 7155c43a5dad4..61276f782e430 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsx).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsx).errors.txt @@ -1,5 +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 'React' to be in scope, but it could not be found. +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) ==== @@ -20,7 +20,7 @@ preact.tsx(3,11): error TS2879: Using JSX fragments requires fragment factory 'R ~~ !!! 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 'React' to be in scope, but it could not be found. +!!! 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 ad4bb3c20b12a..3a1dfd23eb335 100644 --- a/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsxdev).errors.txt +++ b/tests/baselines/reference/jsxJsxsCjsTransformCustomImportPragma(jsx=react-jsxdev).errors.txt @@ -1,5 +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 'React' to be in scope, but it could not be found. +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) ==== @@ -20,7 +20,7 @@ preact.tsx(3,11): error TS2879: Using JSX fragments requires fragment factory 'R ~~ !!! 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 'React' to be in scope, but it could not be found. +!!! 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