Skip to content

Commit f6d2e73

Browse files
authored
add jsx fragments to callLikeExpression (microsoft#59933)
1 parent bd3d700 commit f6d2e73

23 files changed

+488
-167
lines changed

src/compiler/checker.ts

Lines changed: 208 additions & 136 deletions
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3976,6 +3976,10 @@
39763976
"category": "Error",
39773977
"code": 2878
39783978
},
3979+
"Using JSX fragments requires fragment factory '{0}' to be in scope, but it could not be found.": {
3980+
"category": "Error",
3981+
"code": 2879
3982+
},
39793983

39803984
"Import declaration '{0}' is using private name '{1}'.": {
39813985
"category": "Error",
@@ -5530,7 +5534,6 @@
55305534
"category": "Message",
55315535
"code": 6243
55325536
},
5533-
55345537
"Modules": {
55355538
"category": "Message",
55365539
"code": 6244

src/compiler/types.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3126,7 +3126,7 @@ export type CallLikeExpression =
31263126
| NewExpression
31273127
| TaggedTemplateExpression
31283128
| Decorator
3129-
| JsxOpeningLikeElement
3129+
| JsxCallLike
31303130
| InstanceofExpression;
31313131

31323132
export interface AsExpression extends Expression {
@@ -3187,6 +3187,10 @@ export type JsxOpeningLikeElement =
31873187
| JsxSelfClosingElement
31883188
| JsxOpeningElement;
31893189

3190+
export type JsxCallLike =
3191+
| JsxOpeningLikeElement
3192+
| JsxOpeningFragment;
3193+
31903194
export type JsxAttributeLike =
31913195
| JsxAttribute
31923196
| JsxSpreadAttribute;
@@ -6208,19 +6212,18 @@ export interface NodeLinks {
62086212
resolvedType?: Type; // Cached type of type node
62096213
resolvedSignature?: Signature; // Cached signature of signature node or call expression
62106214
resolvedSymbol?: Symbol; // Cached name resolution result
6211-
resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result
62126215
effectsSignature?: Signature; // Signature with possible control flow effects
62136216
enumMemberValue?: EvaluatorResult; // Constant value of enum member
62146217
isVisible?: boolean; // Is this node visible
62156218
containsArgumentsReference?: boolean; // Whether a function-like declaration contains an 'arguments' reference
62166219
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
62176220
jsxFlags: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
62186221
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
6219-
resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element
62206222
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
62216223
switchTypes?: Type[]; // Cached array of switch case expression types
62226224
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
62236225
jsxImplicitImportContainer?: Symbol | false; // Resolved module symbol the implicit jsx import of this file should refer to
6226+
jsxFragmentType?: Type; // Type of the JSX fragment element, set per SourceFile if a jsxFragment is checked in the file
62246227
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive
62256228
deferredNodes?: Set<Node>; // Set of nodes whose checking has been deferred
62266229
capturedBlockScopeBindings?: Symbol[]; // Block-scoped bindings captured beneath this part of an IterationStatement

src/compiler/utilities.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,8 @@ export function getInvokedExpression(node: CallLikeExpression): Expression | Jsx
33833383
return node.tagName;
33843384
case SyntaxKind.BinaryExpression:
33853385
return node.right;
3386+
case SyntaxKind.JsxOpeningFragment:
3387+
return node;
33863388
default:
33873389
return node.expression;
33883390
}

src/compiler/utilitiesPublic.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ import {
204204
JSDocTypedefTag,
205205
JSDocTypeTag,
206206
JsxAttributeLike,
207+
JsxCallLike,
207208
JsxChild,
208209
JsxExpression,
209210
JsxOpeningLikeElement,
@@ -1957,13 +1958,16 @@ export function isCallLikeOrFunctionLikeExpression(node: Node): node is CallLike
19571958

19581959
export function isCallLikeExpression(node: Node): node is CallLikeExpression {
19591960
switch (node.kind) {
1960-
case SyntaxKind.JsxOpeningElement:
1961-
case SyntaxKind.JsxSelfClosingElement:
19621961
case SyntaxKind.CallExpression:
19631962
case SyntaxKind.NewExpression:
19641963
case SyntaxKind.TaggedTemplateExpression:
19651964
case SyntaxKind.Decorator:
1965+
case SyntaxKind.JsxOpeningElement:
1966+
case SyntaxKind.JsxSelfClosingElement:
1967+
case SyntaxKind.JsxOpeningFragment:
19661968
return true;
1969+
case SyntaxKind.BinaryExpression:
1970+
return (node as BinaryExpression).operatorToken.kind === SyntaxKind.InstanceOfKeyword;
19671971
default:
19681972
return false;
19691973
}
@@ -2479,6 +2483,13 @@ export function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeEleme
24792483
|| kind === SyntaxKind.JsxSelfClosingElement;
24802484
}
24812485

2486+
export function isJsxCallLike(node: Node): node is JsxCallLike {
2487+
const kind = node.kind;
2488+
return kind === SyntaxKind.JsxOpeningElement
2489+
|| kind === SyntaxKind.JsxSelfClosingElement
2490+
|| kind === SyntaxKind.JsxOpeningFragment;
2491+
}
2492+
24822493
// Clauses
24832494

24842495
export function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause {

tests/baselines/reference/api/typescript.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5148,7 +5148,7 @@ declare namespace ts {
51485148
interface InstanceofExpression extends BinaryExpression {
51495149
readonly operatorToken: Token<SyntaxKind.InstanceOfKeyword>;
51505150
}
5151-
type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxOpeningLikeElement | InstanceofExpression;
5151+
type CallLikeExpression = CallExpression | NewExpression | TaggedTemplateExpression | Decorator | JsxCallLike | InstanceofExpression;
51525152
interface AsExpression extends Expression {
51535153
readonly kind: SyntaxKind.AsExpression;
51545154
readonly expression: Expression;
@@ -5184,6 +5184,7 @@ declare namespace ts {
51845184
readonly closingElement: JsxClosingElement;
51855185
}
51865186
type JsxOpeningLikeElement = JsxSelfClosingElement | JsxOpeningElement;
5187+
type JsxCallLike = JsxOpeningLikeElement | JsxOpeningFragment;
51875188
type JsxAttributeLike = JsxAttribute | JsxSpreadAttribute;
51885189
type JsxAttributeName = Identifier | JsxNamespacedName;
51895190
type JsxTagNameExpression = Identifier | ThisExpression | JsxTagNamePropertyAccess | JsxNamespacedName;
@@ -8800,6 +8801,7 @@ declare namespace ts {
88008801
function isJsxAttributeLike(node: Node): node is JsxAttributeLike;
88018802
function isStringLiteralOrJsxExpression(node: Node): node is StringLiteral | JsxExpression;
88028803
function isJsxOpeningLikeElement(node: Node): node is JsxOpeningLikeElement;
8804+
function isJsxCallLike(node: Node): node is JsxCallLike;
88038805
function isCaseOrDefaultClause(node: Node): node is CaseOrDefaultClause;
88048806
/** True if node is of a kind that may contain comment text. */
88058807
function isJSDocCommentContainingNode(node: Node): boolean;

tests/baselines/reference/inlineJsxAndJsxFragPragma.errors.txt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
preacty-no-fragment.tsx(5,12): error TS6133: 'Fragment' is declared but its value is never read.
22
preacty-only-fragment-no-jsx.tsx(6,1): error TS2874: This JSX tag requires 'h' to be in scope, but it could not be found.
33
snabbdomy-only-fragment-no-jsx.tsx(4,1): error TS2874: This JSX tag requires 'jsx' to be in scope, but it could not be found.
4+
snabbdomy-only-fragment-no-jsx.tsx(4,1): error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
5+
snabbdomy-only-fragment.tsx(4,1): error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
6+
snabbdomy.tsx(4,1): error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
47

58

69
==== renderer.d.ts (0 errors) ====
@@ -23,11 +26,13 @@ snabbdomy-only-fragment-no-jsx.tsx(4,1): error TS2874: This JSX tag requires 'js
2326
import {h, Fragment} from "./renderer";
2427
<><div></div></>
2528

26-
==== snabbdomy.tsx (0 errors) ====
29+
==== snabbdomy.tsx (1 errors) ====
2730
/* @jsx jsx */
2831
/* @jsxfrag null */
2932
import {jsx} from "./renderer";
3033
<><span></span></>
34+
~~
35+
!!! error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
3136

3237
==== preacty-only-fragment.tsx (0 errors) ====
3338
/**
@@ -37,11 +42,13 @@ snabbdomy-only-fragment-no-jsx.tsx(4,1): error TS2874: This JSX tag requires 'js
3742
import {h, Fragment} from "./renderer";
3843
<></>
3944

40-
==== snabbdomy-only-fragment.tsx (0 errors) ====
45+
==== snabbdomy-only-fragment.tsx (1 errors) ====
4146
/* @jsx jsx */
4247
/* @jsxfrag null */
4348
import {jsx} from "./renderer";
4449
<></>
50+
~~
51+
!!! error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
4552

4653
==== preacty-only-fragment-no-jsx.tsx (1 errors) ====
4754
/**
@@ -53,13 +60,15 @@ snabbdomy-only-fragment-no-jsx.tsx(4,1): error TS2874: This JSX tag requires 'js
5360
~~
5461
!!! error TS2874: This JSX tag requires 'h' to be in scope, but it could not be found.
5562

56-
==== snabbdomy-only-fragment-no-jsx.tsx (1 errors) ====
63+
==== snabbdomy-only-fragment-no-jsx.tsx (2 errors) ====
5764
/* @jsx jsx */
5865
/* @jsxfrag null */
5966
import {} from "./renderer";
6067
<></>
6168
~~
6269
!!! error TS2874: This JSX tag requires 'jsx' to be in scope, but it could not be found.
70+
~~
71+
!!! error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
6372

6473
==== preacty-no-fragment.tsx (1 errors) ====
6574
/**
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
snabbdomy.tsx(6,1): error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
2+
3+
4+
==== react.d.ts (0 errors) ====
5+
declare global {
6+
namespace JSX {
7+
interface IntrinsicElements {
8+
[e: string]: any;
9+
}
10+
}
11+
}
12+
export function createElement(): void;
13+
export function Fragment(): void;
14+
15+
==== preact.d.ts (0 errors) ====
16+
export function h(): void;
17+
export function Frag(): void;
18+
19+
==== snabbdom.d.ts (0 errors) ====
20+
export function h(): void;
21+
22+
==== reacty.tsx (0 errors) ====
23+
import {createElement, Fragment} from "./react";
24+
<><span></span></>
25+
26+
==== preacty.tsx (0 errors) ====
27+
/**
28+
* @jsx h
29+
* @jsxFrag Frag
30+
*/
31+
import {h, Frag} from "./preact";
32+
<><div></div></>
33+
34+
==== snabbdomy.tsx (1 errors) ====
35+
/**
36+
* @jsx h
37+
* @jsxfrag null
38+
*/
39+
import {h} from "./snabbdom";
40+
<><div></div></>
41+
~~
42+
!!! error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
43+
44+
==== mix-n-match.tsx (0 errors) ====
45+
/* @jsx h */
46+
/* @jsxFrag Fragment */
47+
import {h} from "./preact";
48+
import {Fragment} from "./react";
49+
<><span></span></>

tests/baselines/reference/inlineJsxAndJsxFragPragmaOverridesCompilerOptions.types

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ import {createElement, Fragment} from "./react";
4343
> : ^^^^^^
4444

4545
<><span></span></>
46-
><><span></span></> : error
47-
><span></span> : error
46+
><><span></span></> : any
47+
> : ^^^
48+
><span></span> : any
49+
> : ^^^
4850
>span : any
4951
> : ^^^
5052
>span : any
@@ -62,8 +64,10 @@ import {h, Frag} from "./preact";
6264
> : ^^^^^^
6365

6466
<><div></div></>
65-
><><div></div></> : error
66-
><div></div> : error
67+
><><div></div></> : any
68+
> : ^^^
69+
><div></div> : any
70+
> : ^^^
6771
>div : any
6872
> : ^^^
6973
>div : any
@@ -79,8 +83,10 @@ import {h} from "./snabbdom";
7983
> : ^^^^^^
8084

8185
<><div></div></>
82-
><><div></div></> : error
83-
><div></div> : error
86+
><><div></div></> : any
87+
> : ^^^
88+
><div></div> : any
89+
> : ^^^
8490
>div : any
8591
> : ^^^
8692
>div : any
@@ -98,8 +104,10 @@ import {Fragment} from "./react";
98104
> : ^^^^^^
99105

100106
<><span></span></>
101-
><><span></span></> : error
102-
><span></span> : error
107+
><><span></span></> : any
108+
> : ^^^
109+
><span></span> : any
110+
> : ^^^
103111
>span : any
104112
> : ^^^
105113
>span : any

tests/baselines/reference/inlineJsxFactoryWithFragmentIsError.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
index.tsx(3,1): error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
2+
index.tsx(3,1): error TS2879: Using JSX fragments requires fragment factory 'React' to be in scope, but it could not be found.
23
index.tsx(3,1): error TS17017: An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.
34
reacty.tsx(3,1): error TS17017: An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.
45

@@ -19,11 +20,13 @@ reacty.tsx(3,1): error TS17017: An @jsxFrag pragma is required when using an @js
1920
<><h></h></>
2021
~~~~~~~~~~~~
2122
!!! error TS17017: An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.
22-
==== index.tsx (2 errors) ====
23+
==== index.tsx (3 errors) ====
2324
/** @jsx dom */
2425
import { dom } from "./renderer";
2526
<><h></h></>
2627
~~
2728
!!! error TS2874: This JSX tag requires 'React' to be in scope, but it could not be found.
29+
~~
30+
!!! error TS2879: Using JSX fragments requires fragment factory 'React' to be in scope, but it could not be found.
2831
~~~~~~~~~~~~
2932
!!! error TS17017: An @jsxFrag pragma is required when using an @jsx pragma with JSX fragments.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
jsxFactoryAndJsxFragmentFactoryNull.tsx(3,1): error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
2+
3+
4+
==== jsxFactoryAndJsxFragmentFactoryNull.tsx (1 errors) ====
5+
declare var h: any;
6+
7+
<></>;
8+
~~
9+
!!! error TS2879: Using JSX fragments requires fragment factory 'null' to be in scope, but it could not be found.
10+
<><span>1</span><><span>2.1</span><span>2.2</span></></>;

tests/baselines/reference/jsxFactoryAndJsxFragmentFactoryNull.types

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,31 @@
33
=== jsxFactoryAndJsxFragmentFactoryNull.tsx ===
44
declare var h: any;
55
>h : any
6+
> : ^^^
67

78
<></>;
8-
><></> : error
9+
><></> : any
10+
> : ^^^
911

1012
<><span>1</span><><span>2.1</span><span>2.2</span></></>;
11-
><><span>1</span><><span>2.1</span><span>2.2</span></></> : error
12-
><span>1</span> : error
13+
><><span>1</span><><span>2.1</span><span>2.2</span></></> : any
14+
> : ^^^
15+
><span>1</span> : any
16+
> : ^^^
1317
>span : any
1418
> : ^^^
1519
>span : any
1620
> : ^^^
17-
><><span>2.1</span><span>2.2</span></> : error
18-
><span>2.1</span> : error
21+
><><span>2.1</span><span>2.2</span></> : any
22+
> : ^^^
23+
><span>2.1</span> : any
24+
> : ^^^
1925
>span : any
2026
> : ^^^
2127
>span : any
2228
> : ^^^
23-
><span>2.2</span> : error
29+
><span>2.2</span> : any
30+
> : ^^^
2431
>span : any
2532
> : ^^^
2633
>span : any
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
a.tsx(6,28): error TS2322: Type '{ children: () => string; }' is not assignable to type '{ children?: ReactNode; }'.
2+
Types of property 'children' are incompatible.
3+
Type '() => string' is not assignable to type 'ReactNode'.
4+
a.tsx(7,47): error TS2322: Type '() => string' is not assignable to type 'ReactNode'.
5+
6+
7+
==== a.tsx (2 errors) ====
8+
/// <reference path="/.lib/react18/react18.d.ts" />
9+
/// <reference path="/.lib/react18/global.d.ts" />
10+
11+
const test = () => "asd";
12+
13+
const jsxWithJsxFragment = <>{test}</>;
14+
~~
15+
!!! error TS2322: Type '{ children: () => string; }' is not assignable to type '{ children?: ReactNode; }'.
16+
!!! error TS2322: Types of property 'children' are incompatible.
17+
!!! error TS2322: Type '() => string' is not assignable to type 'ReactNode'.
18+
const jsxWithReactFragment = <React.Fragment>{test}</React.Fragment>;
19+
~~~~
20+
!!! error TS2322: Type '() => string' is not assignable to type 'ReactNode'.
21+
!!! related TS6212 a.tsx:7:47: Did you mean to call this expression?
22+

0 commit comments

Comments
 (0)