From 4af0fd56b2b71244f1e322a9bdc03e46f36fba66 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Fri, 24 Jun 2022 10:40:29 -0700 Subject: [PATCH 1/3] Add test case --- .../baselines/reference/issue44596.errors.txt | 29 ++++++++ tests/baselines/reference/issue44596.js | 32 +++++++++ tests/baselines/reference/issue44596.symbols | 68 +++++++++++++++++++ tests/baselines/reference/issue44596.types | 66 ++++++++++++++++++ tests/cases/compiler/issue44596.ts | 26 +++++++ 5 files changed, 221 insertions(+) create mode 100644 tests/baselines/reference/issue44596.errors.txt create mode 100644 tests/baselines/reference/issue44596.js create mode 100644 tests/baselines/reference/issue44596.symbols create mode 100644 tests/baselines/reference/issue44596.types create mode 100644 tests/cases/compiler/issue44596.ts diff --git a/tests/baselines/reference/issue44596.errors.txt b/tests/baselines/reference/issue44596.errors.txt new file mode 100644 index 0000000000000..75821860c3ddb --- /dev/null +++ b/tests/baselines/reference/issue44596.errors.txt @@ -0,0 +1,29 @@ +tests/cases/compiler/index.tsx(11,14): error TS7006: Parameter 'value' implicitly has an 'any' type. + + +==== tests/cases/compiler/index.tsx (1 errors) ==== + interface Elements { + foo: { callback?: (value: number) => void }; + bar: { callback?: (value: string) => void }; + } + + type Props = { as?: C } & Elements[C]; + declare function Test(props: Props): null; + + {}} + ~~~~~ +!!! error TS7006: Parameter 'value' implicitly has an 'any' type. + />; + + Test({ + as: "bar", + callback: (value) => {}, + }); + + + as="bar" + callback={(value) => {}} + />; + \ No newline at end of file diff --git a/tests/baselines/reference/issue44596.js b/tests/baselines/reference/issue44596.js new file mode 100644 index 0000000000000..18b4dec211ccc --- /dev/null +++ b/tests/baselines/reference/issue44596.js @@ -0,0 +1,32 @@ +//// [index.tsx] +interface Elements { + foo: { callback?: (value: number) => void }; + bar: { callback?: (value: string) => void }; +} + +type Props = { as?: C } & Elements[C]; +declare function Test(props: Props): null; + + {}} +/>; + +Test({ + as: "bar", + callback: (value) => {}, +}); + + + as="bar" + callback={(value) => {}} +/>; + + +//// [index.jsx] +; +Test({ + as: "bar", + callback: function (value) { } +}); +; diff --git a/tests/baselines/reference/issue44596.symbols b/tests/baselines/reference/issue44596.symbols new file mode 100644 index 0000000000000..fb315569a7eda --- /dev/null +++ b/tests/baselines/reference/issue44596.symbols @@ -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 = { 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(props: Props): 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 : 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 : 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)) + +/>; + diff --git a/tests/baselines/reference/issue44596.types b/tests/baselines/reference/issue44596.types new file mode 100644 index 0000000000000..841ba1d5b487d --- /dev/null +++ b/tests/baselines/reference/issue44596.types @@ -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 = { as?: C } & Elements[C]; +>Props : Props +>as : C + +declare function Test(props: Props): null; +>Test : (props: Props) => null +>props : Props +>null : null + + {}}/> : any +>Test : (props: Props) => null + + as="bar" +>as : "bar" + + callback={(value) => {}} +>callback : (value: any) => void +>(value) => {} : (value: any) => void +>value : any + +/>; + +Test({ +>Test({ as: "bar", callback: (value) => {},}) : null +>Test : (props: Props) => 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 + +}); + + +> as="bar" callback={(value) => {}}/> : any +>Test : (props: Props) => null + + as="bar" +>as : "bar" + + callback={(value) => {}} +>callback : (value: string) => void +>(value) => {} : (value: string) => void +>value : string + +/>; + diff --git a/tests/cases/compiler/issue44596.ts b/tests/cases/compiler/issue44596.ts new file mode 100644 index 0000000000000..1510e7ef44c09 --- /dev/null +++ b/tests/cases/compiler/issue44596.ts @@ -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 = { as?: C } & Elements[C]; +declare function Test(props: Props): null; + + {}} +/>; + +Test({ + as: "bar", + callback: (value) => {}, +}); + + + as="bar" + callback={(value) => {}} +/>; From 5e0ff3a6b310fd9eb971bd9382ba16183115e36d Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 27 Jun 2022 19:55:16 -0700 Subject: [PATCH 2/3] Pass contextFlags when getting contextual type of JSX elements/attributes --- src/compiler/checker.ts | 20 ++++++------- .../baselines/reference/issue44596.errors.txt | 29 ------------------- tests/baselines/reference/issue44596.types | 10 +++---- 3 files changed, 15 insertions(+), 44 deletions(-) delete mode 100644 tests/baselines/reference/issue44596.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a53c1841b6ef4..9f9d4e3a5f292 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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); diff --git a/tests/baselines/reference/issue44596.errors.txt b/tests/baselines/reference/issue44596.errors.txt deleted file mode 100644 index 75821860c3ddb..0000000000000 --- a/tests/baselines/reference/issue44596.errors.txt +++ /dev/null @@ -1,29 +0,0 @@ -tests/cases/compiler/index.tsx(11,14): error TS7006: Parameter 'value' implicitly has an 'any' type. - - -==== tests/cases/compiler/index.tsx (1 errors) ==== - interface Elements { - foo: { callback?: (value: number) => void }; - bar: { callback?: (value: string) => void }; - } - - type Props = { as?: C } & Elements[C]; - declare function Test(props: Props): null; - - {}} - ~~~~~ -!!! error TS7006: Parameter 'value' implicitly has an 'any' type. - />; - - Test({ - as: "bar", - callback: (value) => {}, - }); - - - as="bar" - callback={(value) => {}} - />; - \ No newline at end of file diff --git a/tests/baselines/reference/issue44596.types b/tests/baselines/reference/issue44596.types index 841ba1d5b487d..6e33aa57d8253 100644 --- a/tests/baselines/reference/issue44596.types +++ b/tests/baselines/reference/issue44596.types @@ -21,16 +21,16 @@ declare function Test(props: Props): null; >null : null {}}/> : any +> {}}/> : error >Test : (props: Props) => null as="bar" >as : "bar" callback={(value) => {}} ->callback : (value: any) => void ->(value) => {} : (value: any) => void ->value : any +>callback : (value: string) => void +>(value) => {} : (value: string) => void +>value : string />; @@ -51,7 +51,7 @@ Test({ }); -> as="bar" callback={(value) => {}}/> : any +> as="bar" callback={(value) => {}}/> : error >Test : (props: Props) => null as="bar" From 98c4e29a34f6c2d98e1e7e4dd9c1a570d5201b84 Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Mon, 27 Jun 2022 20:22:25 -0700 Subject: [PATCH 3/3] Rename test --- .../reference/{issue44596.js => contextuallyTypedJsxAttribute.js} | 0 .../{issue44596.symbols => contextuallyTypedJsxAttribute.symbols} | 0 .../{issue44596.types => contextuallyTypedJsxAttribute.types} | 0 .../compiler/{issue44596.ts => contextuallyTypedJsxAttribute.ts} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename tests/baselines/reference/{issue44596.js => contextuallyTypedJsxAttribute.js} (100%) rename tests/baselines/reference/{issue44596.symbols => contextuallyTypedJsxAttribute.symbols} (100%) rename tests/baselines/reference/{issue44596.types => contextuallyTypedJsxAttribute.types} (100%) rename tests/cases/compiler/{issue44596.ts => contextuallyTypedJsxAttribute.ts} (100%) diff --git a/tests/baselines/reference/issue44596.js b/tests/baselines/reference/contextuallyTypedJsxAttribute.js similarity index 100% rename from tests/baselines/reference/issue44596.js rename to tests/baselines/reference/contextuallyTypedJsxAttribute.js diff --git a/tests/baselines/reference/issue44596.symbols b/tests/baselines/reference/contextuallyTypedJsxAttribute.symbols similarity index 100% rename from tests/baselines/reference/issue44596.symbols rename to tests/baselines/reference/contextuallyTypedJsxAttribute.symbols diff --git a/tests/baselines/reference/issue44596.types b/tests/baselines/reference/contextuallyTypedJsxAttribute.types similarity index 100% rename from tests/baselines/reference/issue44596.types rename to tests/baselines/reference/contextuallyTypedJsxAttribute.types diff --git a/tests/cases/compiler/issue44596.ts b/tests/cases/compiler/contextuallyTypedJsxAttribute.ts similarity index 100% rename from tests/cases/compiler/issue44596.ts rename to tests/cases/compiler/contextuallyTypedJsxAttribute.ts