Skip to content

Commit

Permalink
Pass contextFlags when getting contextual type of JSX elements/attrib…
Browse files Browse the repository at this point in the history
…utes (#49707)
  • Loading branch information
jakebailey authored Jun 28, 2022
1 parent 1eb276f commit b379e7f
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 10 deletions.
20 changes: 10 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27037,8 +27037,8 @@ namespace ts {
return node === conditional.whenTrue || node === conditional.whenFalse ? getContextualType(conditional, contextFlags) : undefined;
}

function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild) {
const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName);
function getContextualTypeForChildJsxExpression(node: JsxElement, child: JsxChild, contextFlags?: ContextFlags) {
const attributesType = getApparentTypeOfContextualType(node.openingElement.tagName, contextFlags);
// JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty)
const jsxChildrenPropertyName = getJsxElementChildrenPropertyName(getJsxNamespaceAt(node));
if (!(attributesType && !isTypeAny(attributesType) && jsxChildrenPropertyName && jsxChildrenPropertyName !== "")) {
Expand All @@ -27057,28 +27057,28 @@ namespace ts {
}, /*noReductions*/ true));
}

function getContextualTypeForJsxExpression(node: JsxExpression): Type | undefined {
function getContextualTypeForJsxExpression(node: JsxExpression, contextFlags?: ContextFlags): Type | undefined {
const exprParent = node.parent;
return isJsxAttributeLike(exprParent)
? getContextualType(node)
? getContextualType(node, contextFlags)
: isJsxElement(exprParent)
? getContextualTypeForChildJsxExpression(exprParent, node)
? getContextualTypeForChildJsxExpression(exprParent, node, contextFlags)
: undefined;
}

function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined {
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute, contextFlags?: ContextFlags): Type | undefined {
// When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type
// which is a type of the parameter of the signature we are trying out.
// If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName
if (isJsxAttribute(attribute)) {
const attributesType = getApparentTypeOfContextualType(attribute.parent);
const attributesType = getApparentTypeOfContextualType(attribute.parent, contextFlags);
if (!attributesType || isTypeAny(attributesType)) {
return undefined;
}
return getTypeOfPropertyOfContextualType(attributesType, attribute.name.escapedText);
}
else {
return getContextualType(attribute.parent);
return getContextualType(attribute.parent, contextFlags);
}
}

Expand Down Expand Up @@ -27272,10 +27272,10 @@ namespace ts {
case SyntaxKind.ExportAssignment:
return tryGetTypeFromEffectiveTypeNode(parent as ExportAssignment);
case SyntaxKind.JsxExpression:
return getContextualTypeForJsxExpression(parent as JsxExpression);
return getContextualTypeForJsxExpression(parent as JsxExpression, contextFlags);
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxSpreadAttribute:
return getContextualTypeForJsxAttribute(parent as JsxAttribute | JsxSpreadAttribute);
return getContextualTypeForJsxAttribute(parent as JsxAttribute | JsxSpreadAttribute, contextFlags);
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxSelfClosingElement:
return getContextualJsxElementAttributesType(parent as JsxOpeningLikeElement, contextFlags);
Expand Down
32 changes: 32 additions & 0 deletions tests/baselines/reference/contextuallyTypedJsxAttribute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [index.tsx]
interface Elements {
foo: { callback?: (value: number) => void };
bar: { callback?: (value: string) => void };
}

type Props<C extends keyof Elements> = { as?: C } & Elements[C];
declare function Test<C extends keyof Elements>(props: Props<C>): null;

<Test
as="bar"
callback={(value) => {}}
/>;

Test({
as: "bar",
callback: (value) => {},
});

<Test<'bar'>
as="bar"
callback={(value) => {}}
/>;


//// [index.jsx]
<Test as="bar" callback={function (value) { }}/>;
Test({
as: "bar",
callback: function (value) { }
});
<Test as="bar" callback={function (value) { }}/>;
68 changes: 68 additions & 0 deletions tests/baselines/reference/contextuallyTypedJsxAttribute.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
=== tests/cases/compiler/index.tsx ===
interface Elements {
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))

foo: { callback?: (value: number) => void };
>foo : Symbol(Elements.foo, Decl(index.tsx, 0, 20))
>callback : Symbol(callback, Decl(index.tsx, 1, 8))
>value : Symbol(value, Decl(index.tsx, 1, 21))

bar: { callback?: (value: string) => void };
>bar : Symbol(Elements.bar, Decl(index.tsx, 1, 46))
>callback : Symbol(callback, Decl(index.tsx, 2, 8))
>value : Symbol(value, Decl(index.tsx, 2, 21))
}

type Props<C extends keyof Elements> = { as?: C } & Elements[C];
>Props : Symbol(Props, Decl(index.tsx, 3, 1))
>C : Symbol(C, Decl(index.tsx, 5, 11))
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
>as : Symbol(as, Decl(index.tsx, 5, 40))
>C : Symbol(C, Decl(index.tsx, 5, 11))
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
>C : Symbol(C, Decl(index.tsx, 5, 11))

declare function Test<C extends keyof Elements>(props: Props<C>): null;
>Test : Symbol(Test, Decl(index.tsx, 5, 64))
>C : Symbol(C, Decl(index.tsx, 6, 22))
>Elements : Symbol(Elements, Decl(index.tsx, 0, 0))
>props : Symbol(props, Decl(index.tsx, 6, 48))
>Props : Symbol(Props, Decl(index.tsx, 3, 1))
>C : Symbol(C, Decl(index.tsx, 6, 22))

<Test
>Test : Symbol(Test, Decl(index.tsx, 5, 64))

as="bar"
>as : Symbol(as, Decl(index.tsx, 8, 5))

callback={(value) => {}}
>callback : Symbol(callback, Decl(index.tsx, 9, 10))
>value : Symbol(value, Decl(index.tsx, 10, 13))

/>;

Test({
>Test : Symbol(Test, Decl(index.tsx, 5, 64))

as: "bar",
>as : Symbol(as, Decl(index.tsx, 13, 6))

callback: (value) => {},
>callback : Symbol(callback, Decl(index.tsx, 14, 12))
>value : Symbol(value, Decl(index.tsx, 15, 13))

});

<Test<'bar'>
>Test : Symbol(Test, Decl(index.tsx, 5, 64))

as="bar"
>as : Symbol(as, Decl(index.tsx, 18, 12))

callback={(value) => {}}
>callback : Symbol(callback, Decl(index.tsx, 19, 10))
>value : Symbol(value, Decl(index.tsx, 20, 13))

/>;

66 changes: 66 additions & 0 deletions tests/baselines/reference/contextuallyTypedJsxAttribute.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
=== tests/cases/compiler/index.tsx ===
interface Elements {
foo: { callback?: (value: number) => void };
>foo : { callback?: (value: number) => void; }
>callback : (value: number) => void
>value : number

bar: { callback?: (value: string) => void };
>bar : { callback?: (value: string) => void; }
>callback : (value: string) => void
>value : string
}

type Props<C extends keyof Elements> = { as?: C } & Elements[C];
>Props : Props<C>
>as : C

declare function Test<C extends keyof Elements>(props: Props<C>): null;
>Test : <C extends keyof Elements>(props: Props<C>) => null
>props : Props<C>
>null : null

<Test
><Test as="bar" callback={(value) => {}}/> : error
>Test : <C extends keyof Elements>(props: Props<C>) => null

as="bar"
>as : "bar"

callback={(value) => {}}
>callback : (value: string) => void
>(value) => {} : (value: string) => void
>value : string

/>;

Test({
>Test({ as: "bar", callback: (value) => {},}) : null
>Test : <C extends keyof Elements>(props: Props<C>) => null
>{ as: "bar", callback: (value) => {},} : { as: "bar"; callback: (value: string) => void; }

as: "bar",
>as : "bar"
>"bar" : "bar"

callback: (value) => {},
>callback : (value: string) => void
>(value) => {} : (value: string) => void
>value : string

});

<Test<'bar'>
><Test<'bar'> as="bar" callback={(value) => {}}/> : error
>Test : <C extends keyof Elements>(props: Props<C>) => null

as="bar"
>as : "bar"

callback={(value) => {}}
>callback : (value: string) => void
>(value) => {} : (value: string) => void
>value : string

/>;

26 changes: 26 additions & 0 deletions tests/cases/compiler/contextuallyTypedJsxAttribute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @jsx: preserve
// @noImplicitAny: true

// @filename: index.tsx
interface Elements {
foo: { callback?: (value: number) => void };
bar: { callback?: (value: string) => void };
}

type Props<C extends keyof Elements> = { as?: C } & Elements[C];
declare function Test<C extends keyof Elements>(props: Props<C>): null;

<Test
as="bar"
callback={(value) => {}}
/>;

Test({
as: "bar",
callback: (value) => {},
});

<Test<'bar'>
as="bar"
callback={(value) => {}}
/>;

0 comments on commit b379e7f

Please sign in to comment.