Skip to content

Fix crash in contextual typing of defaulted element access declarations #33686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}

Expand All @@ -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);
}
}

Expand Down
14 changes: 9 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}
Expand All @@ -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:
Expand Down
13 changes: 8 additions & 5 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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((<PropertyAccessExpression>node.argumentExpression).name));
}
return undefined;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
};
Original file line number Diff line number Diff line change
@@ -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))

};
Original file line number Diff line number Diff line change
@@ -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

};
13 changes: 13 additions & 0 deletions tests/cases/compiler/jsElementAccessNoContextualTypeCrash.ts
Original file line number Diff line number Diff line change
@@ -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;
};