Skip to content

Commit 3af84d4

Browse files
committed
PR feedback
1 parent ff7532c commit 3af84d4

8 files changed

+169
-67
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24309,10 +24309,7 @@ namespace ts {
2430924309
// can be specified by users through attributes property.
2431024310
const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node);
2431124311
const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*inferenceContext*/ undefined, checkMode);
24312-
if (!checkTagNameDoesNotExpectTooManyArguments()) {
24313-
return false;
24314-
}
24315-
return checkTypeRelatedToAndOptionallyElaborate(
24312+
return checkTagNameDoesNotExpectTooManyArguments() && checkTypeRelatedToAndOptionallyElaborate(
2431624313
attributesType,
2431724314
paramType,
2431824315
relation,
@@ -24346,34 +24343,46 @@ namespace ts {
2434624343
return true;
2434724344
}
2434824345

24349-
let hasFirstparamSignatures = false;
24350-
// Check that _some_ first parameter expects and SFC-like thing, and that some overload of the SFC expects an acceptable number of arguments
24346+
let hasFirstParamSignatures = false;
24347+
let maxParamCount = 0;
24348+
// Check that _some_ first parameter expects a FC-like thing, and that some overload of the SFC expects an acceptable number of arguments
2435124349
for (const sig of callSignatures) {
2435224350
const firstparam = getTypeAtPosition(sig, 0);
2435324351
const signaturesOfParam = getSignaturesOfType(firstparam, SignatureKind.Call);
2435424352
if (!length(signaturesOfParam)) continue;
2435524353
for (const paramSig of signaturesOfParam) {
24356-
hasFirstparamSignatures = true;
24354+
hasFirstParamSignatures = true;
2435724355
if (hasEffectiveRestParameter(paramSig)) {
24358-
return true; // some signature has a rest param, so function components can have an aritrary number of arguments
24356+
return true; // some signature has a rest param, so function components can have an arbitrary number of arguments
2435924357
}
2436024358
const paramCount = getParameterCount(paramSig);
24361-
for (const tagSig of tagCallSignatures) {
24362-
const tagParamCount = getParameterCount(tagSig);
24363-
if (tagParamCount <= paramCount) {
24364-
return true; // some signature accepts the number of arguments the function component provides
24365-
}
24359+
if (paramCount > maxParamCount) {
24360+
maxParamCount = paramCount;
2436624361
}
2436724362
}
2436824363
}
24369-
if (!hasFirstparamSignatures) {
24364+
if (!hasFirstParamSignatures) {
2437024365
// Not a single signature had a first parameter which expected a signature - for back compat, and
24371-
// to guard against generic factories which won't have signatures directly, return true
24366+
// to guard against generic factories which won't have signatures directly, do not error
2437224367
return true;
2437324368
}
24369+
let absoluteMinArgCount = Infinity;
24370+
for (const tagSig of tagCallSignatures) {
24371+
const tagRequiredArgCount = getMinArgumentCount(tagSig);
24372+
if (tagRequiredArgCount < absoluteMinArgCount) {
24373+
absoluteMinArgCount = tagRequiredArgCount;
24374+
}
24375+
}
24376+
if (absoluteMinArgCount <= maxParamCount) {
24377+
return true; // some signature accepts the number of arguments the function component provides
24378+
}
2437424379

2437524380
if (reportErrors) {
24376-
const diag = createDiagnosticForNode(node.tagName, Diagnostics.Function_like_tag_expects_more_arguments_than_the_JSX_factory_can_provide);
24381+
const diag = createDiagnosticForNode(node.tagName, Diagnostics.Tag_0_expects_at_least_1_arguments_but_the_JSX_factory_2_provides_at_most_3, entityNameToString(node.tagName), absoluteMinArgCount, entityNameToString(factory), maxParamCount);
24382+
const tagNameDeclaration = getSymbolAtLocation(node.tagName)?.valueDeclaration;
24383+
if (tagNameDeclaration) {
24384+
addRelatedInfo(diag, createDiagnosticForNode(tagNameDeclaration, Diagnostics._0_is_declared_here, entityNameToString(node.tagName)));
24385+
}
2437724386
if (errorOutputContainer && errorOutputContainer.skipLogging) {
2437824387
(errorOutputContainer.errors || (errorOutputContainer.errors = [])).push(diag);
2437924388
}

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4280,7 +4280,7 @@
42804280
"category": "Message",
42814281
"code": 6228
42824282
},
4283-
"Function-like tag expects more arguments than the JSX factory can provide.": {
4283+
"Tag '{0}' expects at least '{1}' arguments, but the JSX factory '{2}' provides at most '{3}'.": {
42844284
"category": "Error",
42854285
"code": 6229
42864286
},

src/compiler/utilities.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -864,14 +864,17 @@ namespace ts {
864864
}
865865
}
866866

867-
export function entityNameToString(name: EntityNameOrEntityNameExpression): string {
867+
export function entityNameToString(name: EntityNameOrEntityNameExpression | JsxTagNameExpression | PrivateIdentifier): string {
868868
switch (name.kind) {
869+
case SyntaxKind.ThisKeyword:
870+
return "this";
871+
case SyntaxKind.PrivateIdentifier:
869872
case SyntaxKind.Identifier:
870873
return getFullWidth(name) === 0 ? idText(name) : getTextOfNode(name);
871874
case SyntaxKind.QualifiedName:
872875
return entityNameToString(name.left) + "." + entityNameToString(name.right);
873876
case SyntaxKind.PropertyAccessExpression:
874-
if (isIdentifier(name.name)) {
877+
if (isIdentifier(name.name) || isPrivateIdentifier(name.name)) {
875878
return entityNameToString(name.expression) + "." + entityNameToString(name.name);
876879
}
877880
else {
Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx(13,12): error TS6229: Function-like tag expects more arguments than the JSX factory can provide.
1+
tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx(19,12): error TS6229: Tag 'MyComp4' expects at least '4' arguments, but the JSX factory 'React.createElement' provides at most '2'.
2+
tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx(20,12): error TS6229: Tag 'MyComp3' expects at least '3' arguments, but the JSX factory 'React.createElement' provides at most '2'.
23

34

4-
==== tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx (1 errors) ====
5+
==== tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx (2 errors) ====
56
/// <reference path="/.lib/react16.d.ts" />
67

78
import * as React from "react";
@@ -10,15 +11,25 @@ tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx(13,12): er
1011
x: number;
1112
}
1213

13-
function MyComp(props: MyProps, context: any, bad: any, verybad: any) {
14+
function MyComp4(props: MyProps, context: any, bad: any, verybad: any) {
15+
return <div></div>;
16+
}
17+
function MyComp3(props: MyProps, context: any, bad: any) {
1418
return <div></div>;
1519
}
16-
17-
const a = <MyComp x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
18-
~~~~~~
19-
!!! error TS6229: Function-like tag expects more arguments than the JSX factory can provide.
20-
2120
function MyComp2(props: MyProps, context: any) {
2221
return <div></div>
2322
}
24-
const b = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
23+
24+
const a = <MyComp4 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
25+
~~~~~~~
26+
!!! error TS6229: Tag 'MyComp4' expects at least '4' arguments, but the JSX factory 'React.createElement' provides at most '2'.
27+
!!! related TS2728 tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx:9:10: 'MyComp4' is declared here.
28+
const b = <MyComp3 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
29+
~~~~~~~
30+
!!! error TS6229: Tag 'MyComp3' expects at least '3' arguments, but the JSX factory 'React.createElement' provides at most '2'.
31+
!!! related TS2728 tests/cases/compiler/jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx:12:10: 'MyComp3' is declared here.
32+
const c = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
33+
34+
declare function MyTagWithOptionalNonJSXBits(props: MyProps, context: any, nonReactArg?: string): JSX.Element;
35+
const d = <MyTagWithOptionalNonJSXBits x={2} />; // Technically OK, but probably questionable

tests/baselines/reference/jsxIssuesErrorWhenTagExpectsTooManyArguments.js

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,38 @@ interface MyProps {
77
x: number;
88
}
99

10-
function MyComp(props: MyProps, context: any, bad: any, verybad: any) {
10+
function MyComp4(props: MyProps, context: any, bad: any, verybad: any) {
11+
return <div></div>;
12+
}
13+
function MyComp3(props: MyProps, context: any, bad: any) {
1114
return <div></div>;
1215
}
13-
14-
const a = <MyComp x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
15-
1616
function MyComp2(props: MyProps, context: any) {
1717
return <div></div>
1818
}
19-
const b = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
19+
20+
const a = <MyComp4 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
21+
const b = <MyComp3 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
22+
const c = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
23+
24+
declare function MyTagWithOptionalNonJSXBits(props: MyProps, context: any, nonReactArg?: string): JSX.Element;
25+
const d = <MyTagWithOptionalNonJSXBits x={2} />; // Technically OK, but probably questionable
2026

2127
//// [jsxIssuesErrorWhenTagExpectsTooManyArguments.js]
2228
"use strict";
2329
/// <reference path="react16.d.ts" />
2430
exports.__esModule = true;
2531
var React = require("react");
26-
function MyComp(props, context, bad, verybad) {
32+
function MyComp4(props, context, bad, verybad) {
33+
return React.createElement("div", null);
34+
}
35+
function MyComp3(props, context, bad) {
2736
return React.createElement("div", null);
2837
}
29-
var a = React.createElement(MyComp, { x: 2 }); // using `MyComp` as a component should error - it expects more arguments than react provides
3038
function MyComp2(props, context) {
3139
return React.createElement("div", null);
3240
}
33-
var b = React.createElement(MyComp2, { x: 2 }); // Should be OK, `context` is allowed, per react rules
41+
var a = React.createElement(MyComp4, { x: 2 }); // using `MyComp` as a component should error - it expects more arguments than react provides
42+
var b = React.createElement(MyComp3, { x: 2 }); // using `MyComp` as a component should error - it expects more arguments than react provides
43+
var c = React.createElement(MyComp2, { x: 2 }); // Should be OK, `context` is allowed, per react rules
44+
var d = React.createElement(MyTagWithOptionalNonJSXBits, { x: 2 }); // Technically OK, but probably questionable

tests/baselines/reference/jsxIssuesErrorWhenTagExpectsTooManyArguments.symbols

Lines changed: 46 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,31 @@ interface MyProps {
1111
>x : Symbol(MyProps.x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 4, 19))
1212
}
1313

14-
function MyComp(props: MyProps, context: any, bad: any, verybad: any) {
15-
>MyComp : Symbol(MyComp, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 6, 1))
16-
>props : Symbol(props, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 16))
14+
function MyComp4(props: MyProps, context: any, bad: any, verybad: any) {
15+
>MyComp4 : Symbol(MyComp4, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 6, 1))
16+
>props : Symbol(props, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 17))
1717
>MyProps : Symbol(MyProps, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 2, 31))
18-
>context : Symbol(context, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 31))
19-
>bad : Symbol(bad, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 45))
20-
>verybad : Symbol(verybad, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 55))
18+
>context : Symbol(context, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 32))
19+
>bad : Symbol(bad, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 46))
20+
>verybad : Symbol(verybad, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 8, 56))
2121

2222
return <div></div>;
2323
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
2424
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
2525
}
26+
function MyComp3(props: MyProps, context: any, bad: any) {
27+
>MyComp3 : Symbol(MyComp3, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 10, 1))
28+
>props : Symbol(props, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 11, 17))
29+
>MyProps : Symbol(MyProps, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 2, 31))
30+
>context : Symbol(context, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 11, 32))
31+
>bad : Symbol(bad, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 11, 46))
2632

27-
const a = <MyComp x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
28-
>a : Symbol(a, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 12, 5))
29-
>MyComp : Symbol(MyComp, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 6, 1))
30-
>x : Symbol(x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 12, 17))
31-
33+
return <div></div>;
34+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
35+
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
36+
}
3237
function MyComp2(props: MyProps, context: any) {
33-
>MyComp2 : Symbol(MyComp2, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 12, 26))
38+
>MyComp2 : Symbol(MyComp2, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 13, 1))
3439
>props : Symbol(props, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 14, 17))
3540
>MyProps : Symbol(MyProps, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 2, 31))
3641
>context : Symbol(context, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 14, 32))
@@ -39,8 +44,33 @@ function MyComp2(props: MyProps, context: any) {
3944
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
4045
>div : Symbol(JSX.IntrinsicElements.div, Decl(react16.d.ts, 2420, 114))
4146
}
42-
const b = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
43-
>b : Symbol(b, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 17, 5))
44-
>MyComp2 : Symbol(MyComp2, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 12, 26))
45-
>x : Symbol(x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 17, 19))
47+
48+
const a = <MyComp4 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
49+
>a : Symbol(a, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 18, 5))
50+
>MyComp4 : Symbol(MyComp4, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 6, 1))
51+
>x : Symbol(x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 18, 18))
52+
53+
const b = <MyComp3 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
54+
>b : Symbol(b, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 19, 5))
55+
>MyComp3 : Symbol(MyComp3, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 10, 1))
56+
>x : Symbol(x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 19, 18))
57+
58+
const c = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
59+
>c : Symbol(c, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 20, 5))
60+
>MyComp2 : Symbol(MyComp2, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 13, 1))
61+
>x : Symbol(x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 20, 19))
62+
63+
declare function MyTagWithOptionalNonJSXBits(props: MyProps, context: any, nonReactArg?: string): JSX.Element;
64+
>MyTagWithOptionalNonJSXBits : Symbol(MyTagWithOptionalNonJSXBits, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 20, 28))
65+
>props : Symbol(props, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 22, 45))
66+
>MyProps : Symbol(MyProps, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 2, 31))
67+
>context : Symbol(context, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 22, 60))
68+
>nonReactArg : Symbol(nonReactArg, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 22, 74))
69+
>JSX : Symbol(JSX, Decl(react16.d.ts, 2367, 12))
70+
>Element : Symbol(JSX.Element, Decl(react16.d.ts, 2368, 23))
71+
72+
const d = <MyTagWithOptionalNonJSXBits x={2} />; // Technically OK, but probably questionable
73+
>d : Symbol(d, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 23, 5))
74+
>MyTagWithOptionalNonJSXBits : Symbol(MyTagWithOptionalNonJSXBits, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 20, 28))
75+
>x : Symbol(x, Decl(jsxIssuesErrorWhenTagExpectsTooManyArguments.tsx, 23, 38))
4676

tests/baselines/reference/jsxIssuesErrorWhenTagExpectsTooManyArguments.types

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ interface MyProps {
99
>x : number
1010
}
1111

12-
function MyComp(props: MyProps, context: any, bad: any, verybad: any) {
13-
>MyComp : (props: MyProps, context: any, bad: any, verybad: any) => JSX.Element
12+
function MyComp4(props: MyProps, context: any, bad: any, verybad: any) {
13+
>MyComp4 : (props: MyProps, context: any, bad: any, verybad: any) => JSX.Element
1414
>props : MyProps
1515
>context : any
1616
>bad : any
@@ -21,14 +21,17 @@ function MyComp(props: MyProps, context: any, bad: any, verybad: any) {
2121
>div : any
2222
>div : any
2323
}
24+
function MyComp3(props: MyProps, context: any, bad: any) {
25+
>MyComp3 : (props: MyProps, context: any, bad: any) => JSX.Element
26+
>props : MyProps
27+
>context : any
28+
>bad : any
2429

25-
const a = <MyComp x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
26-
>a : JSX.Element
27-
><MyComp x={2}/> : JSX.Element
28-
>MyComp : (props: MyProps, context: any, bad: any, verybad: any) => JSX.Element
29-
>x : number
30-
>2 : 2
31-
30+
return <div></div>;
31+
><div></div> : JSX.Element
32+
>div : any
33+
>div : any
34+
}
3235
function MyComp2(props: MyProps, context: any) {
3336
>MyComp2 : (props: MyProps, context: any) => JSX.Element
3437
>props : MyProps
@@ -39,10 +42,39 @@ function MyComp2(props: MyProps, context: any) {
3942
>div : any
4043
>div : any
4144
}
42-
const b = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
45+
46+
const a = <MyComp4 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
47+
>a : JSX.Element
48+
><MyComp4 x={2}/> : JSX.Element
49+
>MyComp4 : (props: MyProps, context: any, bad: any, verybad: any) => JSX.Element
50+
>x : number
51+
>2 : 2
52+
53+
const b = <MyComp3 x={2}/>; // using `MyComp` as a component should error - it expects more arguments than react provides
4354
>b : JSX.Element
55+
><MyComp3 x={2}/> : JSX.Element
56+
>MyComp3 : (props: MyProps, context: any, bad: any) => JSX.Element
57+
>x : number
58+
>2 : 2
59+
60+
const c = <MyComp2 x={2}/>; // Should be OK, `context` is allowed, per react rules
61+
>c : JSX.Element
4462
><MyComp2 x={2}/> : JSX.Element
4563
>MyComp2 : (props: MyProps, context: any) => JSX.Element
4664
>x : number
4765
>2 : 2
4866

67+
declare function MyTagWithOptionalNonJSXBits(props: MyProps, context: any, nonReactArg?: string): JSX.Element;
68+
>MyTagWithOptionalNonJSXBits : (props: MyProps, context: any, nonReactArg?: string) => JSX.Element
69+
>props : MyProps
70+
>context : any
71+
>nonReactArg : string
72+
>JSX : any
73+
74+
const d = <MyTagWithOptionalNonJSXBits x={2} />; // Technically OK, but probably questionable
75+
>d : JSX.Element
76+
><MyTagWithOptionalNonJSXBits x={2} /> : JSX.Element
77+
>MyTagWithOptionalNonJSXBits : (props: MyProps, context: any, nonReactArg?: string) => JSX.Element
78+
>x : number
79+
>2 : 2
80+

0 commit comments

Comments
 (0)