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

Baseline arity checks for jsx sfc tags #36643

Merged
merged 1 commit into from
Feb 25, 2020

Conversation

weswigham
Copy link
Member

Fixes the worst cases of #33104 without going so far as #29818 (which is terrible for performance).

In this change, we lookup the jsx factory function during jsx signature resolution, and check if any overload expects a function-ish thing; if one does, we validate that, for at least one of those overloads, the number of arguments the FC being used as the tag is less than or equal to the number provided by the JSX library.

This will not cause an error in the two-argument case for react, as react specifies FCs can have a second argument of type any for the react context. So the actual example from the original report in #33104 would be completely uncaught (and there's really nothing we can do about it, since the function actually looks like a completely valid FC), however the more egregious examples down-thread of 3+ arguments do get caught.

Notably, I have not made this contingent on strictFunctionTypes - normally our signature arity rules outside of strictFunctionTypes mode would allow the assignment (function bivariance, wooo), were we actually assigning the tag type to the argument type, but that does not come into play here, as I am decomposing the types and checking argument counts directly.

cc @DanielRosenwasser for error message feedback~

@weswigham
Copy link
Member Author

@typescript-bot cherrypick this into master

typescript-bot pushed a commit to typescript-bot/TypeScript that referenced this pull request Feb 5, 2020
Component commits:
388801e Baseline arity checks for jsx sfc tags
@typescript-bot
Copy link
Collaborator

Hey @weswigham, I've opened #36644 for you.

src/compiler/checker.ts Outdated Show resolved Hide resolved
@@ -4280,6 +4280,10 @@
"category": "Message",
"code": 6228
},
"Function-like tag expects more arguments than the JSX factory can provide.": {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"Function-like tag expects more arguments than the JSX factory can provide.": {
"'{0}' expects at least '{1}' arguments, but the JSX factory '{2}' provides at most '{3}'.": {

Copy link
Member Author

Choose a reason for hiding this comment

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

at least isn't quite right for the current implementation - at least right now, we're never looking at minimum argument count here. For example, I could write

declare function MyTagWithOptionalNonJSXBits(props: Props, context: any, nonReactArg?: string): JSX.Element;

and what's here right now would issue an error on that tag's usage, and I very much question if it's OK to feed that into React... Minimally, we know React will never provide that third argument...

Copy link
Member Author

@weswigham weswigham Feb 6, 2020

Choose a reason for hiding this comment

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

OK, I've adjusted the logic to match your message, even if I think it allows some questionable tags through.

const paramCount = getParameterCount(paramSig);
for (const tagSig of tagCallSignatures) {
const tagParamCount = getParameterCount(tagSig);
if (tagParamCount <= paramCount) {
Copy link
Member

Choose a reason for hiding this comment

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

This arity check is very simple and differs a lot from our other logic. It'll make better errors easier, but are we sure it's okay?

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's fine; it's pretty conservative. Since we're going through like two calls and then checking some overload is maybe OK (rather than doing overload resolution), and not validating any argument types beyond the first (that's what #29818 does), choosing to only issue an error when we're absolutely sure something's wrong seems reasonable.

Copy link
Member

@sandersn sandersn left a comment

Choose a reason for hiding this comment

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

Lots of spelling nits but overall looks good.

src/compiler/checker.ts Show resolved Hide resolved
src/compiler/diagnosticMessages.json Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
src/compiler/checker.ts Outdated Show resolved Hide resolved
@weswigham
Copy link
Member Author

cc @DanielRosenwasser @sandersn for updated review statuses~

@weswigham
Copy link
Member Author

ping @sandersn @DanielRosenwasser again

@sandersn
Copy link
Member

(I noticed that this is opened against 3.8 so needs to be retargetted to master or something like that.)

Finish comment

PR feedback
@weswigham weswigham changed the base branch from release-3.8 to master February 24, 2020 23:33
@weswigham
Copy link
Member Author

@sandersn I've retargeted this PR to master and squashed/rebased it. Once the build is green, it should be good to go.

@weswigham weswigham merged commit 7d8dc73 into microsoft:master Feb 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants