Skip to content

add jsx fragments to callLikeExpression - #59933

Merged
Isabel Duan (iisaduan) merged 19 commits into
microsoft:mainfrom
iisaduan:typeJsxFragment
Sep 27, 2024
Merged

add jsx fragments to callLikeExpression#59933
Isabel Duan (iisaduan) merged 19 commits into
microsoft:mainfrom
iisaduan:typeJsxFragment

Conversation

@iisaduan

@iisaduanIsabel Duan (iisaduan) commented Sep 10, 2024

Copy link
Copy Markdown
Member

This PR adds type checking to the children of JSX fragments. Fixes#50429.

  • Adds JsxOpeningFragment to CallLikeExpression by defining JsxCallLike.
    • JsxOpeningFragment is not added to JsxOpenlingLikeFragment because it requires many more changes than necessary for this typecheck, and causes crashes in expression checking. More details here.
  • Updates to NodeLinks:
    • Adds jsxFragmentType to store the type of the fragment at the SourceFile level, since a different fragment factory can be referenced per file.
    • Removes resolvedJsxElementAllAttributesType and resolvedIndexInfo (since both have been unused since at least 2018??)
  • Fixes isCallLikeExpression to account for InstanceOfExpression
  • New error TS2876: Using JSX fragments requires fragment factory {0} to be in scope, but it could not be found. We previously did not issue an error if a fragment factory was set through a pragma or compiler option, and the specified factory could not be resolved.

@typescript-bot

Copy link
Copy Markdown
Contributor

Looks like you're introducing a change to the public API surface area. If this includes breaking changes, please document them on our wiki's API Breaking Changes page.

Also, please make sure Daniel Rosenwasser (@DanielRosenwasser) and Ryan Cavanaugh (@RyanCavanaugh) are aware of the changes, just as a heads up.

@iisaduan

Copy link
Copy Markdown
MemberAuthor

TypeScript Bot (@typescript-bot) test it

@typescript-bot

TypeScript Bot (typescript-bot) commented Sep 10, 2024

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
test top400✅ Started👀 Results
user test this✅ Started✅ Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@typescript-bot

Copy link
Copy Markdown
Contributor

Hey Isabel Duan (@iisaduan), the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot

Copy link
Copy Markdown
Contributor

Isabel Duan (@iisaduan)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Compiler-Unions - node (v18.15.0, x64)
Errors3030~~~p=1.000 n=6
Symbols62,15362,153~~~p=1.000 n=6
Types50,24250,242~~~p=1.000 n=6
Memory used193,642k (± 0.98%)193,020k (± 0.75%)~192,407k195,978kp=1.000 n=6
Parse Time1.31s (± 0.39%)1.31s (± 1.04%)~1.29s1.33sp=0.794 n=6
Bind Time0.71s0.71s (± 0.57%)~0.71s0.72sp=0.405 n=6
Check Time9.57s (± 0.59%)9.59s (± 0.29%)~9.56s9.62sp=0.687 n=6
Emit Time2.72s (± 0.36%)2.71s (± 0.56%)~2.69s2.73sp=0.865 n=6
Total Time14.30s (± 0.43%)14.32s (± 0.19%)~14.28s14.36sp=0.572 n=6
angular-1 - node (v18.15.0, x64)
Errors77~~~p=1.000 n=6
Symbols945,753945,753~~~p=1.000 n=6
Types410,067410,067~~~p=1.000 n=6
Memory used1,222,715k (± 0.00%)1,222,735k (± 0.01%)~1,222,686k1,222,853kp=0.936 n=6
Parse Time7.92s (± 1.12%)7.95s (± 0.54%)~7.88s8.01sp=0.419 n=6
Bind Time2.21s (± 0.37%)2.22s (± 0.44%)~2.20s2.23sp=0.282 n=6
Check Time36.27s (± 0.36%)36.28s (± 0.27%)~36.11s36.39sp=1.000 n=6
Emit Time17.80s (± 0.25%)17.79s (± 0.20%)~17.74s17.85sp=0.466 n=6
Total Time64.21s (± 0.26%)64.25s (± 0.16%)~64.05s64.31sp=0.748 n=6
mui-docs - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols2,515,5502,515,550~~~p=1.000 n=6
Types933,023933,023~~~p=1.000 n=6
Memory used2,350,335k (± 0.00%)2,350,385k (± 0.00%)~2,350,308k2,350,478kp=0.230 n=6
Parse Time11.16s (± 0.25%)11.17s (± 0.35%)~11.12s11.21sp=0.936 n=6
Bind Time2.62s (± 0.45%)2.61s (± 0.77%)~2.58s2.63sp=0.568 n=6
Check Time85.87s (± 0.64%)85.96s (± 0.35%)~85.58s86.39sp=0.936 n=6
Emit Time0.33s (± 1.23%)0.33s (± 1.58%)~0.32s0.33sp=0.114 n=6
Total Time99.97s (± 0.56%)100.06s (± 0.29%)~99.64s100.43sp=0.936 n=6
self-build-src - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,250,2131,250,310+97 (+ 0.01%)~~p=0.001 n=6
Types265,146265,169+23 (+ 0.01%)~~p=0.001 n=6
Memory used2,402,810k (± 0.02%)2,403,540k (± 0.03%)~2,402,797k2,404,728kp=0.093 n=6
Parse Time5.11s (± 0.66%)5.06s (± 0.54%)-0.04s (- 0.78%)5.03s5.11sp=0.030 n=6
Bind Time1.90s (± 0.40%)1.91s (± 0.57%)~1.90s1.93sp=0.195 n=6
Check Time34.82s (± 0.21%)34.92s (± 0.45%)~34.74s35.21sp=0.149 n=6
Emit Time3.34s (± 1.10%)3.31s (± 1.11%)~3.24s3.34sp=0.197 n=6
Total Time45.16s (± 0.25%)45.20s (± 0.43%)~45.00s45.56sp=1.000 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,250,2131,250,310+97 (+ 0.01%)~~p=0.001 n=6
Types265,146265,169+23 (+ 0.01%)~~p=0.001 n=6
Memory used2,477,372k (± 0.01%)2,477,306k (± 0.02%)~2,476,901k2,477,817kp=0.575 n=6
Parse Time5.28s (± 0.92%)5.25s (± 0.45%)~5.21s5.28sp=0.221 n=6
Bind Time1.71s (± 0.71%)1.71s (± 0.37%)~1.70s1.72sp=0.673 n=6
Check Time35.13s (± 0.31%)35.19s (± 0.18%)~35.12s35.27sp=0.230 n=6
Emit Time3.36s (± 0.59%)3.37s (± 1.25%)~3.31s3.43sp=0.807 n=6
Total Time45.47s (± 0.34%)45.53s (± 0.15%)~45.42s45.61sp=0.471 n=6
self-compiler - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols259,449259,480+31 (+ 0.01%)~~p=0.001 n=6
Types105,986106,008+22 (+ 0.02%)~~p=0.001 n=6
Memory used433,756k (± 0.02%)433,930k (± 0.02%)+174k (+ 0.04%)433,806k433,989kp=0.013 n=6
Parse Time2.86s (± 0.60%)2.86s (± 1.08%)~2.81s2.89sp=0.871 n=6
Bind Time1.08s (± 1.98%)1.07s (± 1.15%)~1.06s1.09sp=0.085 n=6
Check Time15.36s (± 0.50%)15.40s (± 0.35%)~15.35s15.48sp=0.470 n=6
Emit Time1.39s (± 1.74%)1.40s (± 1.35%)~1.37s1.42sp=0.746 n=6
Total Time20.70s (± 0.35%)20.72s (± 0.33%)~20.64s20.82sp=0.630 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors6868~~~p=1.000 n=6
Symbols225,018225,018~~~p=1.000 n=6
Types94,24994,249~~~p=1.000 n=6
Memory used370,233k (± 0.02%)370,288k (± 0.02%)~370,199k370,416kp=0.173 n=6
Parse Time2.76s (± 0.79%)2.77s (± 0.87%)~2.73s2.80sp=0.332 n=6
Bind Time1.57s (± 0.80%)1.58s (± 1.01%)~1.56s1.60sp=0.216 n=6
Check Time15.76s (± 0.25%)15.78s (± 0.43%)~15.69s15.86sp=0.688 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time20.09s (± 0.28%)20.13s (± 0.32%)~20.05s20.23sp=0.423 n=6
vscode - node (v18.15.0, x64)
Errors11~~~p=1.000 n=6
Symbols3,052,3143,052,314~~~p=1.000 n=6
Types1,055,6201,055,620~~~p=1.000 n=6
Memory used3,161,910k (± 0.00%)3,161,904k (± 0.00%)~3,161,818k3,161,943kp=1.000 n=6
Parse Time13.90s (± 0.41%)13.88s (± 0.15%)~13.85s13.90sp=0.627 n=6
Bind Time4.35s (± 0.31%)4.36s (± 0.37%)~4.34s4.38sp=0.324 n=6
Check Time81.89s (± 0.38%)81.55s (± 0.36%)~81.03s81.86sp=0.128 n=6
Emit Time22.12s (± 0.50%)22.18s (± 0.55%)~22.01s22.34sp=0.470 n=6
Total Time122.27s (± 0.25%)121.97s (± 0.24%)~121.49s122.32sp=0.173 n=6
webpack - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols277,156277,156~~~p=1.000 n=6
Types112,946112,946~~~p=1.000 n=6
Memory used427,020k (± 0.04%)426,862k (± 0.01%)~426,821k426,954kp=0.093 n=6
Parse Time4.89s (± 0.46%)4.88s (± 0.56%)~4.83s4.91sp=0.746 n=6
Bind Time2.15s (± 0.49%)2.13s (± 0.69%)~2.11s2.15sp=0.139 n=6
Check Time21.77s (± 0.39%)21.78s (± 0.38%)~21.64s21.86sp=0.688 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time28.80s (± 0.28%)28.80s (± 0.21%)~28.69s28.86sp=1.000 n=6
xstate-main - node (v18.15.0, x64)
Errors03🔻+3 (+ ∞%)~~p=0.001 n=6
Symbols539,317539,496+179 (+ 0.03%)~~p=0.001 n=6
Types178,997179,139+142 (+ 0.08%)~~p=0.001 n=6
Memory used484,193k (± 0.04%)484,133k (± 0.01%)~484,017k484,237kp=0.810 n=6
Parse Time4.27s (± 0.55%)4.23s (± 0.39%)-0.04s (- 0.94%)4.20s4.25sp=0.014 n=6
Bind Time1.55s (± 1.11%)1.55s (± 1.00%)~1.52s1.56sp=1.000 n=6
Check Time22.55s (± 0.34%)22.69s (± 0.27%)+0.14s (+ 0.61%)22.62s22.79sp=0.020 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time28.36s (± 0.28%)28.46s (± 0.18%)+0.10s (+ 0.35%)28.41s28.52sp=0.030 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@typescript-bot

Copy link
Copy Markdown
Contributor

Isabel Duan (@iisaduan) Here are the results of running the user tests with tsc comparing main and refs/pull/59933/merge:

Everything looks good!

Comment threadsrc/compiler/types.ts
Comment on lines +3190 to +3192
export type JsxCallLike =
| JsxOpeningLikeElement
| JsxOpeningFragment;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldnt it make sense to include JsxOpeningFragment as part of JsxOpeningLikeElement?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isabel Duan (@iisaduan) can you address that suggestion?

@iisaduanIsabel Duan (iisaduan)Sep 26, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delay in response. It's a good question and I wanted something detailed for PR description.

TLDR keeping a type that has only opening element and self closing element, is still beneficial, and adding JsxOpeningFragment to JsxOpeningLikeElement requires a much bigger change than is needed to add this fragment type check. The biggest reason is because this add causes crashes in expression checking. We would either need to rework what fragments are, in a way that is making them almost identical to an JsxOpeningElement, or we would need to update the majority of places JsxOpeningLikeElement is referenced to ignore fragments anyway (and if we don't want to ignore them, we'd need to handle fragments specially, which could cause us to do redundant/unnecessary work).

In my first iteration of this PR, I did try to add fragments to openingLike, and the biggest issue was that we run into crashes during expression checking (probably due to several series of no longer correct assertions). Since the fragment check is now fully added, I tried again to fix these crashes over the last day or so, but I still ran into layers of them, so it just ends up being much more work than is necessary for this type check.

Also regarding the use of JsxOpeningLikeElement-- they have many more checks than what we need to check fragments, so adding fragments would require us to then ignore fragments in many uses of openinglike. For example, after this PR, just in checker, there are ~30 functions that would still error because of fragment incompatibilty with what JsxOpeningLikeElement is currently. These functions would almost always be updated to ignore fragments anyways, since ~20 are related to checking tagName or attributes. While the rest may have checks applicable to fragments, they aren't performing any different checks than what we already do (so we should probably ignore fragments because we don't need to do the work for them again). This is without taking into account other parts of the compiler/services, and anyone else using JsxOpeningLikeElement

Comment threadsrc/compiler/utilitiesPublic.ts
@typescript-bot

Copy link
Copy Markdown
Contributor

Isabel Duan (@iisaduan) Here are the results of running the top 400 repos with tsc comparing main and refs/pull/59933/merge:

Something interesting changed - please have a look.

Details

adobe/react-spectrum

9 of 15 projects failed to build with the old tsc and were ignored

starters/tailwind/tsconfig.json

  • error TS2322: Type '{ children: (Element | ReactNode | ((values: CheckboxRenderProps & { defaultChildren: ReactNode; }) => ReactNode))[]; }' is not assignable to type '{ children?: ReactNode; }'.
  • error TS2322: Type '{ children: (Element | ReactNode | ((values: GridListItemRenderProps & { defaultChildren: ReactNode; }) => ReactNode))[]; }' is not assignable to type '{ children?: ReactNode; }'.
  • error TS2322: Type '{ children: (Element | ReactNode | ((values: RadioRenderProps & { defaultChildren: ReactNode; }) => ReactNode))[]; }' is not assignable to type '{ children?: ReactNode; }'.
  • error TS2322: Type '{ children: (Element | ReactNode | ((values: TagRenderProps & { defaultChildren: ReactNode; }) => ReactNode))[]; }' is not assignable to type '{ children?: ReactNode; }'.

react-hook-form/react-hook-form

2 of 3 projects failed to build with the old tsc and were ignored

tsconfig.json

  • error TS2322: Type '{ children: (string | Element | ("message" extends keyof FieldError & keyof DeepRequired<T>[string] ? [FieldError["message" & keyof DeepRequired<...>[string]], FieldErrorsImpl<...>["message" & keyof DeepRequired<...>[string]]] extends [...] ? Merge<...> : FieldError["message" & keyof DeepRequired<...>[string]] | Fie...' is not assignable to type '{ children?: ReactNode; }'.
  • error TS2322: Type '{ children: (FieldError | Element | undefined)[]; }' is not assignable to type '{ children?: ReactNode; }'.

Comment threadsrc/compiler/checker.ts Outdated
Comment threadsrc/compiler/checker.ts Outdated
const errorNode = isJsxOpeningFragment(node) ? node : node.tagName;
const errorEntityName = isJsxOpeningFragment(node) ? "Fragment" : entityNameToString(node.tagName);
const diag = createDiagnosticForNode(errorNode, Diagnostics.Tag_0_expects_at_least_1_arguments_but_the_JSX_factory_2_provides_at_most_3, errorEntityName, absoluteMinArgCount, entityNameToString(factory), maxParamCount);
const tagNameDeclaration = isJsxOpeningFragment(node) ? getJSXFragmentType(node).symbol.valueDeclaration : getSymbolAtLocation(node.tagName)?.valueDeclaration;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than writing Tag 'Fragment' expects at least..., should we specialize the message to just Fragments expect at least... for the fragment case? Since they're really not named tags, per sey.

@iisaduanIsabel Duan (iisaduan)Sep 26, 2024

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I ended up reverting this change. I did some testing, and even with proper checking of fragments in the rest of checkTagNameDoesNotExpectTooManyArguments, it doesn't seem possible to hit this error message with a fragment. Hitting this error message requires attributes to be passed in through the tag (example <TagName x={/*stuff*/}>). For just an opening element, if there are no attributes in the tag (<TagName>), we won't ever hit this error because we issue missing property or not assignable errors instead. So even if Fragment is defined to always take multiple arguments, we won't ever get this error

@iisaduan
Isabel Duan (iisaduan) merged commit f6d2e73 into microsoft:mainSep 27, 2024
@iisaduan
Isabel Duan (iisaduan) deleted the typeJsxFragment branch September 27, 2024 22:52
Nathan Shively-Sanders (sandersn) added a commit to sandersn/DefinitelyTyped that referenced this pull request Oct 3, 2024
Typescript 5.7 will fail unless usage of RaxFragment is explicit, with
an import of it and a `/** @jsxFrag RaxFragment */` pragma at the top of
the file. This PR adds that.
RaxFragment also needs to have an exported value. I wasn't sure what to
use here so I copied preact, since preact is used in the Typescript
handbook as an example:
```ts
export const RaxFragment: FunctionComponent<{}>
```
Notably, though, the preact source has a comment indicating that
*they're* not sure of the correct type either.
Here is the Typescript 5.7 PR that made this error appear:
microsoft/TypeScript#59933
Nathan Shively-Sanders (sandersn) added a commit to sandersn/DefinitelyTyped that referenced this pull request Oct 3, 2024
It now requires an explicit import of React in order to use JSX
fragments.
Here is the Typescript 5.7 PR that introduced this error:
microsoft/TypeScript#59933
Nathan Shively-Sanders (sandersn) added a commit to sandersn/DefinitelyTyped that referenced this pull request Oct 3, 2024
It now requires an explicit import of React in order to use JSX fragments.
Here is the Typescript 5.7 PR that introduced this error:
microsoft/TypeScript#59933
@microsoftMicrosoft (microsoft) locked as resolved and limited conversation to collaborators Oct 16, 2025
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Author: TeamFor Milestone BugPRs that fix a bug with a specific milestone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JSX Fragments are in-properly typed when not using React.Fragment

6 participants

@iisaduan@typescript-bot@DanielRosenwasser@weswigham@Andarist@jakebailey