diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 63791096192..47950566dfd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2966,7 +2966,7 @@ namespace ts { } else { const symbol = lookupSymbolForPropertyAccess(node.expression); - return symbol && symbol.exports && symbol.exports.get(escapeLeadingUnderscores(getElementOrPropertyAccessName(node))); + return symbol && symbol.exports && symbol.exports.get(getElementOrPropertyAccessName(node)); } } @@ -2979,7 +2979,7 @@ namespace ts { } else { const s = forEachIdentifierInEntityName(e.expression, parent, action); - return action(getNameOrArgument(e), s && s.exports && s.exports.get(escapeLeadingUnderscores(getElementOrPropertyAccessName(e))), s); + return action(getNameOrArgument(e), s && s.exports && s.exports.get(getElementOrPropertyAccessName(e)), s); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0303668818b..eb3f51da2c9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -20940,7 +20940,7 @@ namespace ts { if (!decl) { return false; } - const lhs = binaryExpression.left as PropertyAccessExpression; + const lhs = cast(binaryExpression.left, isAccessExpression); const overallAnnotation = getEffectiveTypeAnnotationNode(decl); if (overallAnnotation) { return getTypeFromTypeNode(overallAnnotation); @@ -20951,8 +20951,11 @@ namespace ts { if (parentSymbol) { const annotated = getEffectiveTypeAnnotationNode(parentSymbol.valueDeclaration); if (annotated) { - const type = getTypeOfPropertyOfContextualType(getTypeFromTypeNode(annotated), lhs.name.escapedText); - return type || false; + const nameStr = getElementOrPropertyAccessName(lhs); + if (nameStr !== undefined) { + const type = getTypeOfPropertyOfContextualType(getTypeFromTypeNode(annotated), nameStr); + return type || false; + } } return false; } @@ -20972,12 +20975,13 @@ namespace ts { } } if (kind === AssignmentDeclarationKind.ModuleExports) return false; - const thisAccess = binaryExpression.left as PropertyAccessExpression; + const thisAccess = cast(binaryExpression.left, isAccessExpression); if (!isObjectLiteralMethod(getThisContainer(thisAccess.expression, /*includeArrowFunctions*/ false))) { return false; } const thisType = checkThisExpression(thisAccess.expression); - return thisType && getTypeOfPropertyOfContextualType(thisType, thisAccess.name.escapedText) || false; + const nameStr = getElementOrPropertyAccessName(thisAccess); + return nameStr !== undefined && thisType && getTypeOfPropertyOfContextualType(thisType, nameStr) || false; case AssignmentDeclarationKind.ObjectDefinePropertyValue: case AssignmentDeclarationKind.ObjectDefinePropertyExports: case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 63650297da2..a0988cfa7e3 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -2128,18 +2128,21 @@ namespace ts { } /* @internal */ - export function getElementOrPropertyAccessName(node: LiteralLikeElementAccessExpression | PropertyAccessExpression): string; - export function getElementOrPropertyAccessName(node: AccessExpression): string | undefined; - export function getElementOrPropertyAccessName(node: AccessExpression): string | undefined { + export function getElementOrPropertyAccessName(node: LiteralLikeElementAccessExpression | PropertyAccessExpression): __String; + export function getElementOrPropertyAccessName(node: AccessExpression): __String | undefined; + export function getElementOrPropertyAccessName(node: AccessExpression): __String | undefined { const name = getElementOrPropertyAccessArgumentExpressionOrName(node); if (name) { if (isIdentifier(name)) { - return idText(name); + return name.escapedText; } if (isStringLiteralLike(name) || isNumericLiteral(name)) { - return name.text; + return escapeLeadingUnderscores(name.text); } } + if (isElementAccessExpression(node) && isWellKnownSymbolSyntactically(node.argumentExpression)) { + return getPropertyNameForKnownSymbolName(idText((node.argumentExpression).name)); + } return undefined; } diff --git a/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.errors.txt b/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.errors.txt new file mode 100644 index 00000000000..0d64700447b --- /dev/null +++ b/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/jsElementAccessNoContextualTypeCrash.js(2,1): error TS2741: Property 'localize' is missing in type '{}' but required in type 'typeof Common'. + + +==== tests/cases/compiler/jsElementAccessNoContextualTypeCrash.js (1 errors) ==== + var Common = {}; + self['Common'] = self['Common'] || {}; + ~~~~~~~~~~~~~~ +!!! error TS2741: Property 'localize' is missing in type '{}' but required in type 'typeof Common'. +!!! related TS2728 tests/cases/compiler/jsElementAccessNoContextualTypeCrash.js:7:1: 'localize' is declared here. + /** + * @param {string} string + * @return {string} + */ + Common.localize = function (string) { + return string; + }; \ No newline at end of file diff --git a/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.symbols b/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.symbols new file mode 100644 index 00000000000..b14b0a0dd79 --- /dev/null +++ b/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.symbols @@ -0,0 +1,24 @@ +=== tests/cases/compiler/jsElementAccessNoContextualTypeCrash.js === +var Common = {}; +>Common : Symbol(Common, Decl(jsElementAccessNoContextualTypeCrash.js, 0, 3), Decl(jsElementAccessNoContextualTypeCrash.js, 1, 38)) + +self['Common'] = self['Common'] || {}; +>self : Symbol(self, Decl(lib.dom.d.ts, --, --), Decl(jsElementAccessNoContextualTypeCrash.js, 0, 16)) +>'Common' : Symbol(Common, Decl(jsElementAccessNoContextualTypeCrash.js, 0, 3), Decl(jsElementAccessNoContextualTypeCrash.js, 1, 38)) +>self : Symbol(self, Decl(lib.dom.d.ts, --, --), Decl(jsElementAccessNoContextualTypeCrash.js, 0, 16)) +>'Common' : Symbol(Common, Decl(jsElementAccessNoContextualTypeCrash.js, 0, 3), Decl(jsElementAccessNoContextualTypeCrash.js, 1, 38)) + +/** + * @param {string} string + * @return {string} + */ +Common.localize = function (string) { +>Common.localize : Symbol(Common.localize, Decl(jsElementAccessNoContextualTypeCrash.js, 1, 38)) +>Common : Symbol(Common, Decl(jsElementAccessNoContextualTypeCrash.js, 0, 3), Decl(jsElementAccessNoContextualTypeCrash.js, 1, 38)) +>localize : Symbol(Common.localize, Decl(jsElementAccessNoContextualTypeCrash.js, 1, 38)) +>string : Symbol(string, Decl(jsElementAccessNoContextualTypeCrash.js, 6, 28)) + + return string; +>string : Symbol(string, Decl(jsElementAccessNoContextualTypeCrash.js, 6, 28)) + +}; diff --git a/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.types b/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.types new file mode 100644 index 00000000000..860743446e1 --- /dev/null +++ b/tests/baselines/reference/jsElementAccessNoContextualTypeCrash.types @@ -0,0 +1,32 @@ +=== tests/cases/compiler/jsElementAccessNoContextualTypeCrash.js === +var Common = {}; +>Common : typeof Common +>{} : {} + +self['Common'] = self['Common'] || {}; +>self['Common'] = self['Common'] || {} : {} +>self['Common'] : typeof Common +>self : Window & typeof globalThis +>'Common' : "Common" +>self['Common'] || {} : {} +>self['Common'] : typeof Common +>self : Window & typeof globalThis +>'Common' : "Common" +>{} : {} + +/** + * @param {string} string + * @return {string} + */ +Common.localize = function (string) { +>Common.localize = function (string) { return string;} : (string: string) => string +>Common.localize : (string: string) => string +>Common : typeof Common +>localize : (string: string) => string +>function (string) { return string;} : (string: string) => string +>string : string + + return string; +>string : string + +}; diff --git a/tests/cases/compiler/jsElementAccessNoContextualTypeCrash.ts b/tests/cases/compiler/jsElementAccessNoContextualTypeCrash.ts new file mode 100644 index 00000000000..3a4e66da499 --- /dev/null +++ b/tests/cases/compiler/jsElementAccessNoContextualTypeCrash.ts @@ -0,0 +1,13 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true +// @filename: jsElementAccessNoContextualTypeCrash.js +var Common = {}; +self['Common'] = self['Common'] || {}; +/** + * @param {string} string + * @return {string} + */ +Common.localize = function (string) { + return string; +}; \ No newline at end of file