@@ -4881,7 +4881,7 @@ namespace ts {
4881
4881
let jsdocType: Type | undefined;
4882
4882
let types: Type[] | undefined;
4883
4883
for (const declaration of symbol.declarations) {
4884
- const expression = isBinaryExpression(declaration) ? declaration :
4884
+ const expression = ( isBinaryExpression(declaration) || isCallExpression(declaration) ) ? declaration :
4885
4885
isPropertyAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration :
4886
4886
undefined;
4887
4887
if (!expression) {
@@ -4897,9 +4897,11 @@ namespace ts {
4897
4897
definedInMethod = true;
4898
4898
}
4899
4899
}
4900
- jsdocType = getJSDocTypeFromAssignmentDeclaration(jsdocType, expression, symbol, declaration);
4900
+ if (!isCallExpression(expression)) {
4901
+ jsdocType = getJSDocTypeFromAssignmentDeclaration(jsdocType, expression, symbol, declaration);
4902
+ }
4901
4903
if (!jsdocType) {
4902
- (types || (types = [])).push(isBinaryExpression(expression) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType);
4904
+ (types || (types = [])).push(( isBinaryExpression(expression) || isCallExpression(expression) ) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType);
4903
4905
}
4904
4906
}
4905
4907
let type = jsdocType;
@@ -4960,7 +4962,32 @@ namespace ts {
4960
4962
}
4961
4963
4962
4964
/** If we don't have an explicit JSDoc type, get the type from the initializer. */
4963
- function getInitializerTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: BinaryExpression, kind: AssignmentDeclarationKind) {
4965
+ function getInitializerTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: BinaryExpression | CallExpression, kind: AssignmentDeclarationKind) {
4966
+ if (isCallExpression(expression)) {
4967
+ if (resolvedSymbol) {
4968
+ return getTypeOfSymbol(resolvedSymbol); // This shouldn't happen except under some hopefully forbidden merges of export assignments and object define assignments
4969
+ }
4970
+ const objectLitType = checkExpressionCached((expression as BindableObjectDefinePropertyCall).arguments[2]);
4971
+ const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String);
4972
+ if (valueType) {
4973
+ return valueType;
4974
+ }
4975
+ const getFunc = getTypeOfPropertyOfType(objectLitType, "get" as __String);
4976
+ if (getFunc) {
4977
+ const getSig = getSingleCallSignature(getFunc);
4978
+ if (getSig) {
4979
+ return getReturnTypeOfSignature(getSig);
4980
+ }
4981
+ }
4982
+ const setFunc = getTypeOfPropertyOfType(objectLitType, "set" as __String);
4983
+ if (setFunc) {
4984
+ const setSig = getSingleCallSignature(setFunc);
4985
+ if (setSig) {
4986
+ return getTypeOfFirstParameterOfSignature(setSig);
4987
+ }
4988
+ }
4989
+ return anyType;
4990
+ }
4964
4991
const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : getWidenedLiteralType(checkExpressionCached(expression.right));
4965
4992
if (type.flags & TypeFlags.Object &&
4966
4993
kind === AssignmentDeclarationKind.ModuleExports &&
@@ -5212,7 +5239,7 @@ namespace ts {
5212
5239
}
5213
5240
let type: Type | undefined;
5214
5241
if (isInJSFile(declaration) &&
5215
- (isBinaryExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent))) {
5242
+ (isCallExpression(declaration) || isBinaryExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent))) {
5216
5243
type = getWidenedTypeFromAssignmentDeclaration(symbol);
5217
5244
}
5218
5245
else if (isJSDocPropertyLikeTag(declaration)
@@ -16179,6 +16206,31 @@ namespace ts {
16179
16206
getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.Prototype) {
16180
16207
return (container.parent.parent.parent.left as PropertyAccessExpression).expression;
16181
16208
}
16209
+ // Object.defineProperty(x, "method", { value: function() { } });
16210
+ // Object.defineProperty(x, "method", { set: (x: () => void) => void });
16211
+ // Object.defineProperty(x, "method", { get: () => function() { }) });
16212
+ else if (container.kind === SyntaxKind.FunctionExpression &&
16213
+ isPropertyAssignment(container.parent) &&
16214
+ isIdentifier(container.parent.name) &&
16215
+ (container.parent.name.escapedText === "value" || container.parent.name.escapedText === "get" || container.parent.name.escapedText === "set") &&
16216
+ isObjectLiteralExpression(container.parent.parent) &&
16217
+ isCallExpression(container.parent.parent.parent) &&
16218
+ container.parent.parent.parent.arguments[2] === container.parent.parent &&
16219
+ getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) {
16220
+ return (container.parent.parent.parent.arguments[0] as PropertyAccessExpression).expression;
16221
+ }
16222
+ // Object.defineProperty(x, "method", { value() { } });
16223
+ // Object.defineProperty(x, "method", { set(x: () => void) {} });
16224
+ // Object.defineProperty(x, "method", { get() { return () => {} } });
16225
+ else if (isMethodDeclaration(container) &&
16226
+ isIdentifier(container.name) &&
16227
+ (container.name.escapedText === "value" || container.name.escapedText === "get" || container.name.escapedText === "set") &&
16228
+ isObjectLiteralExpression(container.parent) &&
16229
+ isCallExpression(container.parent.parent) &&
16230
+ container.parent.parent.arguments[2] === container.parent &&
16231
+ getAssignmentDeclarationKind(container.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) {
16232
+ return (container.parent.parent.arguments[0] as PropertyAccessExpression).expression;
16233
+ }
16182
16234
}
16183
16235
16184
16236
function getTypeForThisExpressionFromJSDoc(node: Node) {
@@ -16741,6 +16793,10 @@ namespace ts {
16741
16793
}
16742
16794
const thisType = checkThisExpression(thisAccess.expression);
16743
16795
return thisType && getTypeOfPropertyOfContextualType(thisType, thisAccess.name.escapedText) || false;
16796
+ case AssignmentDeclarationKind.ObjectDefinePropertyValue:
16797
+ case AssignmentDeclarationKind.ObjectDefinePropertyExports:
16798
+ case AssignmentDeclarationKind.ObjectDefinePrototypeProperty:
16799
+ return Debug.fail("Does not apply");
16744
16800
default:
16745
16801
return Debug.assertNever(kind);
16746
16802
}
@@ -21132,18 +21188,52 @@ namespace ts {
21132
21188
return true;
21133
21189
}
21134
21190
21191
+ function isReadonlyAssignmentDeclaration(d: Declaration) {
21192
+ if (!isCallExpression(d)) {
21193
+ return false;
21194
+ }
21195
+ if (!isBindableObjectDefinePropertyCall(d)) {
21196
+ return false;
21197
+ }
21198
+ const objectLitType = checkExpressionCached(d.arguments[2]);
21199
+ const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String);
21200
+ if (valueType) {
21201
+ const writableProp = getPropertyOfType(objectLitType, "writable" as __String);
21202
+ const writableType = writableProp && getTypeOfSymbol(writableProp);
21203
+ if (!writableType || writableType === falseType || writableType === regularFalseType) {
21204
+ return true;
21205
+ }
21206
+ // We include this definition whereupon we walk back and check the type at the declaration because
21207
+ // The usual definition of `Object.defineProperty` will _not_ cause literal types to be preserved in the
21208
+ // argument types, should the type be contextualized by the call itself.
21209
+ if (writableProp && writableProp.valueDeclaration && isPropertyAssignment(writableProp.valueDeclaration)) {
21210
+ const initializer = writableProp.valueDeclaration.initializer;
21211
+ const rawOriginalType = checkExpression(initializer);
21212
+ if (rawOriginalType === falseType || rawOriginalType === regularFalseType) {
21213
+ return true;
21214
+ }
21215
+ }
21216
+ return false;
21217
+ }
21218
+ const setProp = getPropertyOfType(objectLitType, "set" as __String);
21219
+ return !setProp;
21220
+ }
21221
+
21135
21222
function isReadonlySymbol(symbol: Symbol): boolean {
21136
21223
// The following symbols are considered read-only:
21137
21224
// Properties with a 'readonly' modifier
21138
21225
// Variables declared with 'const'
21139
21226
// Get accessors without matching set accessors
21140
21227
// Enum members
21228
+ // Object.defineProperty assignments with writable false or no setter
21141
21229
// Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation)
21142
21230
return !!(getCheckFlags(symbol) & CheckFlags.Readonly ||
21143
21231
symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly ||
21144
21232
symbol.flags & SymbolFlags.Variable && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const ||
21145
21233
symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) ||
21146
- symbol.flags & SymbolFlags.EnumMember);
21234
+ symbol.flags & SymbolFlags.EnumMember ||
21235
+ some(symbol.declarations, isReadonlyAssignmentDeclaration)
21236
+ );
21147
21237
}
21148
21238
21149
21239
function isReferenceToReadonlyEntity(expr: Expression, symbol: Symbol): boolean {
0 commit comments