diff --git a/src/services/completions.ts b/src/services/completions.ts index 1bf57d33fd..91da6c7da1 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -898,8 +898,8 @@ namespace ts.Completions { return undefined; } - const { parent, kind } = contextToken; - if (kind === SyntaxKind.DotToken) { + let parent = contextToken.parent; + if (contextToken.kind === SyntaxKind.DotToken) { if (parent.kind === SyntaxKind.PropertyAccessExpression) { node = (contextToken.parent).expression; isRightOfDot = true; @@ -915,16 +915,24 @@ namespace ts.Completions { } } else if (sourceFile.languageVariant === LanguageVariant.JSX) { - switch (contextToken.parent.kind) { + // + // If the tagname is a property access expression, we will then walk up to the top most of property access expression. + // Then, try to get a JSX container and its associated attributes type. + if (parent && parent.kind === SyntaxKind.PropertyAccessExpression) { + contextToken = parent; + parent = parent.parent; + } + + switch (parent.kind) { case SyntaxKind.JsxClosingElement: - if (kind === SyntaxKind.SlashToken) { + if (contextToken.kind === SyntaxKind.SlashToken) { isStartingCloseTag = true; location = contextToken; } break; case SyntaxKind.BinaryExpression: - if (!((contextToken.parent as BinaryExpression).left.flags & NodeFlags.ThisNodeHasError)) { + if (!((parent as BinaryExpression).left.flags & NodeFlags.ThisNodeHasError)) { // It has a left-hand side, so we're not in an opening JSX tag. break; } @@ -933,7 +941,7 @@ namespace ts.Completions { case SyntaxKind.JsxSelfClosingElement: case SyntaxKind.JsxElement: case SyntaxKind.JsxOpeningElement: - if (kind === SyntaxKind.LessThanToken) { + if (contextToken.kind === SyntaxKind.LessThanToken) { isRightOfOpenTag = true; location = contextToken; } @@ -1401,6 +1409,7 @@ namespace ts.Completions { case SyntaxKind.LessThanSlashToken: case SyntaxKind.SlashToken: case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: case SyntaxKind.JsxAttributes: case SyntaxKind.JsxAttribute: case SyntaxKind.JsxSpreadAttribute: diff --git a/tests/cases/fourslash/tsxCompletion14.ts b/tests/cases/fourslash/tsxCompletion14.ts new file mode 100644 index 0000000000..32fbaef71f --- /dev/null +++ b/tests/cases/fourslash/tsxCompletion14.ts @@ -0,0 +1,44 @@ +/// +//@module: commonjs +//@jsx: preserve + +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// } +//// interface ElementAttributesProperty { props; } +//// } + +//@Filename: exporter.tsx +//// export class Thing { props: { ONE: string; TWO: number } } +//// export module M { +//// export declare function SFCComp(props: { Three: number; Four: string }): JSX.Element; +//// } + +//@Filename: file.tsx +//// import * as Exp from './exporter'; +//// var x1 = ; +//// var x2 = ; +//// var x3 = ; +//// var x4 = ; + + +goTo.marker("1"); +verify.completionListCount(2); +verify.completionListContains('ONE'); +verify.completionListContains('TWO'); + +goTo.marker("2"); +verify.completionListCount(2); +verify.completionListContains("Three"); +verify.completionListContains("Four"); + +goTo.marker("3"); +verify.completionListCount(2); +verify.completionListContains('ONE'); +verify.completionListContains('TWO'); + +goTo.marker("4"); +verify.completionListCount(2); +verify.completionListContains("Three"); +verify.completionListContains("Four");