Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add jsx fragments to callLikeExpression #59933

Merged
merged 19 commits into from
Sep 27, 2024
Merged

Conversation

iisaduan
Copy link
Member

@iisaduan iisaduan commented Sep 10, 2024

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 typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels Sep 10, 2024
@typescript-bot
Copy link
Collaborator

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 @DanielRosenwasser and @RyanCavanaugh are aware of the changes, just as a heads up.

@iisaduan
Copy link
Member Author

@typescript-bot test it

@typescript-bot
Copy link
Collaborator

typescript-bot commented Sep 10, 2024

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

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

@typescript-bot
Copy link
Collaborator

Hey @iisaduan, the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot
Copy link
Collaborator

@iisaduan
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - node (v18.15.0, x64)
Errors 30 30 ~ ~ ~ p=1.000 n=6
Symbols 62,153 62,153 ~ ~ ~ p=1.000 n=6
Types 50,242 50,242 ~ ~ ~ p=1.000 n=6
Memory used 193,642k (± 0.98%) 193,020k (± 0.75%) ~ 192,407k 195,978k p=1.000 n=6
Parse Time 1.31s (± 0.39%) 1.31s (± 1.04%) ~ 1.29s 1.33s p=0.794 n=6
Bind Time 0.71s 0.71s (± 0.57%) ~ 0.71s 0.72s p=0.405 n=6
Check Time 9.57s (± 0.59%) 9.59s (± 0.29%) ~ 9.56s 9.62s p=0.687 n=6
Emit Time 2.72s (± 0.36%) 2.71s (± 0.56%) ~ 2.69s 2.73s p=0.865 n=6
Total Time 14.30s (± 0.43%) 14.32s (± 0.19%) ~ 14.28s 14.36s p=0.572 n=6
angular-1 - node (v18.15.0, x64)
Errors 7 7 ~ ~ ~ p=1.000 n=6
Symbols 945,753 945,753 ~ ~ ~ p=1.000 n=6
Types 410,067 410,067 ~ ~ ~ p=1.000 n=6
Memory used 1,222,715k (± 0.00%) 1,222,735k (± 0.01%) ~ 1,222,686k 1,222,853k p=0.936 n=6
Parse Time 7.92s (± 1.12%) 7.95s (± 0.54%) ~ 7.88s 8.01s p=0.419 n=6
Bind Time 2.21s (± 0.37%) 2.22s (± 0.44%) ~ 2.20s 2.23s p=0.282 n=6
Check Time 36.27s (± 0.36%) 36.28s (± 0.27%) ~ 36.11s 36.39s p=1.000 n=6
Emit Time 17.80s (± 0.25%) 17.79s (± 0.20%) ~ 17.74s 17.85s p=0.466 n=6
Total Time 64.21s (± 0.26%) 64.25s (± 0.16%) ~ 64.05s 64.31s p=0.748 n=6
mui-docs - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 2,515,550 2,515,550 ~ ~ ~ p=1.000 n=6
Types 933,023 933,023 ~ ~ ~ p=1.000 n=6
Memory used 2,350,335k (± 0.00%) 2,350,385k (± 0.00%) ~ 2,350,308k 2,350,478k p=0.230 n=6
Parse Time 11.16s (± 0.25%) 11.17s (± 0.35%) ~ 11.12s 11.21s p=0.936 n=6
Bind Time 2.62s (± 0.45%) 2.61s (± 0.77%) ~ 2.58s 2.63s p=0.568 n=6
Check Time 85.87s (± 0.64%) 85.96s (± 0.35%) ~ 85.58s 86.39s p=0.936 n=6
Emit Time 0.33s (± 1.23%) 0.33s (± 1.58%) ~ 0.32s 0.33s p=0.114 n=6
Total Time 99.97s (± 0.56%) 100.06s (± 0.29%) ~ 99.64s 100.43s p=0.936 n=6
self-build-src - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,250,213 1,250,310 +97 (+ 0.01%) ~ ~ p=0.001 n=6
Types 265,146 265,169 +23 (+ 0.01%) ~ ~ p=0.001 n=6
Memory used 2,402,810k (± 0.02%) 2,403,540k (± 0.03%) ~ 2,402,797k 2,404,728k p=0.093 n=6
Parse Time 5.11s (± 0.66%) 5.06s (± 0.54%) -0.04s (- 0.78%) 5.03s 5.11s p=0.030 n=6
Bind Time 1.90s (± 0.40%) 1.91s (± 0.57%) ~ 1.90s 1.93s p=0.195 n=6
Check Time 34.82s (± 0.21%) 34.92s (± 0.45%) ~ 34.74s 35.21s p=0.149 n=6
Emit Time 3.34s (± 1.10%) 3.31s (± 1.11%) ~ 3.24s 3.34s p=0.197 n=6
Total Time 45.16s (± 0.25%) 45.20s (± 0.43%) ~ 45.00s 45.56s p=1.000 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 1,250,213 1,250,310 +97 (+ 0.01%) ~ ~ p=0.001 n=6
Types 265,146 265,169 +23 (+ 0.01%) ~ ~ p=0.001 n=6
Memory used 2,477,372k (± 0.01%) 2,477,306k (± 0.02%) ~ 2,476,901k 2,477,817k p=0.575 n=6
Parse Time 5.28s (± 0.92%) 5.25s (± 0.45%) ~ 5.21s 5.28s p=0.221 n=6
Bind Time 1.71s (± 0.71%) 1.71s (± 0.37%) ~ 1.70s 1.72s p=0.673 n=6
Check Time 35.13s (± 0.31%) 35.19s (± 0.18%) ~ 35.12s 35.27s p=0.230 n=6
Emit Time 3.36s (± 0.59%) 3.37s (± 1.25%) ~ 3.31s 3.43s p=0.807 n=6
Total Time 45.47s (± 0.34%) 45.53s (± 0.15%) ~ 45.42s 45.61s p=0.471 n=6
self-compiler - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 259,449 259,480 +31 (+ 0.01%) ~ ~ p=0.001 n=6
Types 105,986 106,008 +22 (+ 0.02%) ~ ~ p=0.001 n=6
Memory used 433,756k (± 0.02%) 433,930k (± 0.02%) +174k (+ 0.04%) 433,806k 433,989k p=0.013 n=6
Parse Time 2.86s (± 0.60%) 2.86s (± 1.08%) ~ 2.81s 2.89s p=0.871 n=6
Bind Time 1.08s (± 1.98%) 1.07s (± 1.15%) ~ 1.06s 1.09s p=0.085 n=6
Check Time 15.36s (± 0.50%) 15.40s (± 0.35%) ~ 15.35s 15.48s p=0.470 n=6
Emit Time 1.39s (± 1.74%) 1.40s (± 1.35%) ~ 1.37s 1.42s p=0.746 n=6
Total Time 20.70s (± 0.35%) 20.72s (± 0.33%) ~ 20.64s 20.82s p=0.630 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors 68 68 ~ ~ ~ p=1.000 n=6
Symbols 225,018 225,018 ~ ~ ~ p=1.000 n=6
Types 94,249 94,249 ~ ~ ~ p=1.000 n=6
Memory used 370,233k (± 0.02%) 370,288k (± 0.02%) ~ 370,199k 370,416k p=0.173 n=6
Parse Time 2.76s (± 0.79%) 2.77s (± 0.87%) ~ 2.73s 2.80s p=0.332 n=6
Bind Time 1.57s (± 0.80%) 1.58s (± 1.01%) ~ 1.56s 1.60s p=0.216 n=6
Check Time 15.76s (± 0.25%) 15.78s (± 0.43%) ~ 15.69s 15.86s p=0.688 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 20.09s (± 0.28%) 20.13s (± 0.32%) ~ 20.05s 20.23s p=0.423 n=6
vscode - node (v18.15.0, x64)
Errors 1 1 ~ ~ ~ p=1.000 n=6
Symbols 3,052,314 3,052,314 ~ ~ ~ p=1.000 n=6
Types 1,055,620 1,055,620 ~ ~ ~ p=1.000 n=6
Memory used 3,161,910k (± 0.00%) 3,161,904k (± 0.00%) ~ 3,161,818k 3,161,943k p=1.000 n=6
Parse Time 13.90s (± 0.41%) 13.88s (± 0.15%) ~ 13.85s 13.90s p=0.627 n=6
Bind Time 4.35s (± 0.31%) 4.36s (± 0.37%) ~ 4.34s 4.38s p=0.324 n=6
Check Time 81.89s (± 0.38%) 81.55s (± 0.36%) ~ 81.03s 81.86s p=0.128 n=6
Emit Time 22.12s (± 0.50%) 22.18s (± 0.55%) ~ 22.01s 22.34s p=0.470 n=6
Total Time 122.27s (± 0.25%) 121.97s (± 0.24%) ~ 121.49s 122.32s p=0.173 n=6
webpack - node (v18.15.0, x64)
Errors 0 0 ~ ~ ~ p=1.000 n=6
Symbols 277,156 277,156 ~ ~ ~ p=1.000 n=6
Types 112,946 112,946 ~ ~ ~ p=1.000 n=6
Memory used 427,020k (± 0.04%) 426,862k (± 0.01%) ~ 426,821k 426,954k p=0.093 n=6
Parse Time 4.89s (± 0.46%) 4.88s (± 0.56%) ~ 4.83s 4.91s p=0.746 n=6
Bind Time 2.15s (± 0.49%) 2.13s (± 0.69%) ~ 2.11s 2.15s p=0.139 n=6
Check Time 21.77s (± 0.39%) 21.78s (± 0.38%) ~ 21.64s 21.86s p=0.688 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 28.80s (± 0.28%) 28.80s (± 0.21%) ~ 28.69s 28.86s p=1.000 n=6
xstate-main - node (v18.15.0, x64)
Errors 0 3 🔻+3 (+ ∞%) ~ ~ p=0.001 n=6
Symbols 539,317 539,496 +179 (+ 0.03%) ~ ~ p=0.001 n=6
Types 178,997 179,139 +142 (+ 0.08%) ~ ~ p=0.001 n=6
Memory used 484,193k (± 0.04%) 484,133k (± 0.01%) ~ 484,017k 484,237k p=0.810 n=6
Parse Time 4.27s (± 0.55%) 4.23s (± 0.39%) -0.04s (- 0.94%) 4.20s 4.25s p=0.014 n=6
Bind Time 1.55s (± 1.11%) 1.55s (± 1.00%) ~ 1.52s 1.56s p=1.000 n=6
Check Time 22.55s (± 0.34%) 22.69s (± 0.27%) +0.14s (+ 0.61%) 22.62s 22.79s p=0.020 n=6
Emit Time 0.00s 0.00s ~ ~ ~ p=1.000 n=6
Total Time 28.36s (± 0.28%) 28.46s (± 0.18%) +0.10s (+ 0.35%) 28.41s 28.52s p=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)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@typescript-bot
Copy link
Collaborator

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

Everything looks good!

Comment on lines +3190 to +3192
export type JsxCallLike =
| JsxOpeningLikeElement
| JsxOpeningFragment;
Copy link
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?

Copy link
Member

Choose a reason for hiding this comment

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

@iisaduan can you address that suggestion?

Copy link
Member Author

@iisaduan iisaduan Sep 26, 2024

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

@typescript-bot
Copy link
Collaborator

@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; }'.

@iisaduan iisaduan marked this pull request as ready for review September 24, 2024 16:59
src/compiler/checker.ts Show resolved Hide resolved
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
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.

Copy link
Member Author

@iisaduan iisaduan Sep 26, 2024

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 iisaduan requested a review from weswigham September 27, 2024 06:52
@iisaduan iisaduan merged commit f6d2e73 into microsoft:main Sep 27, 2024
31 checks passed
@iisaduan iisaduan deleted the typeJsxFragment branch September 27, 2024 22:52
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
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
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs 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
5 participants