mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-04-17 13:48:46 -05:00
Pass contextFlags when getting contextual type of JSX elements/attributes (#49707)
This commit is contained in:
@@ -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 !== "")) {
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
32
tests/baselines/reference/contextuallyTypedJsxAttribute.js
Normal file
32
tests/baselines/reference/contextuallyTypedJsxAttribute.js
Normal file
@@ -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) { }}/>;
|
||||
@@ -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))
|
||||
|
||||
/>;
|
||||
|
||||
@@ -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
tests/cases/compiler/contextuallyTypedJsxAttribute.ts
Normal file
26
tests/cases/compiler/contextuallyTypedJsxAttribute.ts
Normal file
@@ -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) => {}}
|
||||
/>;
|
||||
Reference in New Issue
Block a user