-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
[Master] Type checking JSX children #15160
Conversation
…t we won't include in typ-checking
…eScript into master-jsxChildren # Conflicts: # src/compiler/scanner.ts
src/compiler/checker.ts
Outdated
@@ -424,6 +424,8 @@ namespace ts { | |||
IntrinsicClassAttributes: "IntrinsicClassAttributes" | |||
}; | |||
|
|||
const jsxChildrenPropertyName = "children"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i would say we should treat this the same way we do with props
instead of hard coding it.
src/compiler/checker.ts
Outdated
@@ -8676,8 +8680,9 @@ namespace ts { | |||
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean { | |||
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) { | |||
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes); | |||
const containsSynthesizedJsxChildren = !!(source.flags & TypeFlags.ContainsSynthesizedJsxChildren); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do not think we need this here
src/compiler/diagnosticMessages.json
Outdated
@@ -2099,6 +2099,10 @@ | |||
"category": "Error", | |||
"code": 2707 | |||
}, | |||
"props.children are specified twice. The attribute named 'children' will be overwritten.": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
name for children
should be variable, and i would not include props
here.
… error if you use children without specified such property
# Conflicts: # src/compiler/checker.ts # src/compiler/diagnosticMessages.json
ping @RyanCavanaugh @mhegazy |
src/compiler/checker.ts
Outdated
@@ -423,6 +423,9 @@ namespace ts { | |||
|
|||
let _jsxNamespace: string; | |||
let _jsxFactoryEntity: EntityName; | |||
let _jsxElementAttribPropInterfaceSymbol: Symbol; // JSX.ElementAttributesProperty [symbol] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment would be good here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(A longer one explaining it, that is)
src/compiler/checker.ts
Outdated
// interface ElementAttributesProperty { | ||
// props: { | ||
// children?: any; | ||
// }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's figure out some other way to do this - this is too opaque and might not combine well with future work
src/compiler/checker.ts
Outdated
error(attribsPropTypeSym.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, JsxNames.ElementAttributesPropertyNameContainer); | ||
return undefined; | ||
// No interface exists, so the element attributes type will be an implicit any | ||
_jsxElementPropertiesName = undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this mean we'll keep doing this logic repeatedly?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep if we can't find the JSXElement. May be it is better to be empty string to indicate that we already done this
src/compiler/checker.ts
Outdated
@@ -14574,7 +14672,7 @@ namespace ts { | |||
// We can figure that out by resolving attributes property and check number of properties in the resolved type | |||
// If the call has correct arity, we will then check if the argument type and parameter type is assignable | |||
|
|||
const callIsIncomplete = node.attributes.end === node.end; // If we are missing the close "/>", the call is incomplete | |||
const callIsIncomplete = node.attributes.end === node.end; // If we are missing the close "/>", the call is incoplete |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undo spelling regression
@@ -366,7 +366,7 @@ namespace ts { | |||
return computeLineAndCharacterOfPosition(getLineStarts(sourceFile), position); | |||
} | |||
|
|||
export function isWhiteSpace(ch: number): boolean { | |||
export function isWhiteSpaceLike(ch: number): boolean { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the rename?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because it is not just WhiteSpace but Whitespace and empty line.
GTG after lint fixes |
Thanks @RyanCavanaugh for reviewing 🍰 🍰 |
Thank you! This is a great feature. |
@yuit can you write up some minimal docs? I can fill in some details but would like you to write down the basics |
The issue that this is meant to resolve has examples that restrict the type of the components in children, but I don't see an example in this PR that does that. Is it actually possible? For example, in the linked issue there is a a use-case where a |
I see you've also been checking on SO: https://stackoverflow.com/questions/44475309/how-do-i-restrict-the-type-of-react-children-in-typescript-using-the-newly-adde I think that the issue here is that all you really get back from a rendered component is a JSX Element. There's not really any information in thre to specify where the element came from. I'm not sure if @yuit or @RyanCavanaugh have had any thoughts on this. |
yeah, I have become skeptical that what I'm trying to do is possible, which makes me think that #13618 should not have been closed, since it is specifically about getting type restrictions on what kind of components are used as children of a react component. But I could very well be missing something! |
Yeah, this is what I saw in my tests, too. You can't say something like this: import React, { Component } from 'react';
import { render } from 'react-dom';
class Foo extends Component<{}, void> {
render() {
return <p>Hello from Foo!</p>;
}
}
interface BarProps {
children?: Foo | Array<Foo>;
}
const Bar = ({ children }: BarProps) => <div>This must be Foo(s): {children}.</div>;
render(
<div>
<Bar>
<Foo /> {/* invalid - is treated as `JSX.Element`, not `Foo` */}
</Bar>
</div>,
document.getElementById('app')
); |
If this really can't be done, then I suggest re-opening #13618, since it's specifically about typing the components used as children (should I comment as such there?) |
any "last words" on this folks ?
cc @yuit @DanielRosenwasser @RyanCavanaugh thx! |
Would love to see support for typing children! 👍 |
Fix #13618 : type checking children property (for examples and motivation see the original issue) Thanks @joelday for prototyping the feature!
Details
Look for "props" in JSX.ElementChildrenAttribute if there exist a property inside(by default in react.d.ts it is
children
) that property name will be used as a name of children property. If such property doesn't exist (e.g using older react.d.ts), we will just check all the children but will NOT attach the type as "children" property.If there existed children in the body of JSX expression, synthesize symbol presenting those children and add them as part of JSX attributes named
children
. If there alreadychildren
specified as attribute of opening element there will be an error indicating that the attribute will get overwritten.When we check each child, we call into
checkExpression
what this mean is that all JSX Expression will have type JSX.Element. In the future, by treating as JSX.Element will allow us to easily extends for doing these works [JSX] Cannot declare JSX elements to be in specific types. #13746 or Type JSX elements based on createElement function #14729.....Examples
Note: with the
<Fetchuser>
example, we can now provide contextual type and give completion as well (see fourslash test)