From 66a5531cf8a919c4ee6570d35eced04be9cf3286 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Tue, 18 Sep 2018 17:33:25 -0700 Subject: [PATCH 1/4] Add new special assignment kinds for recognizing Object.defineProperty calls --- src/compiler/binder.ts | 60 ++- src/compiler/checker.ts | 76 +++- src/compiler/types.ts | 13 +- src/compiler/utilities.ts | 56 ++- src/services/navigationBar.ts | 2 + src/services/utilities.ts | 2 + .../reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- ...heckExportsObjectAssignProperty.errors.txt | 110 ++++++ .../checkExportsObjectAssignProperty.symbols | 276 ++++++++++++++ .../checkExportsObjectAssignProperty.types | 360 ++++++++++++++++++ .../checkObjectDefineProperty.errors.txt | 73 ++++ .../checkObjectDefineProperty.symbols | 180 +++++++++ .../reference/checkObjectDefineProperty.types | 232 +++++++++++ .../jsdoc/checkExportsObjectAssignProperty.ts | 82 ++++ .../jsdoc/checkObjectDefineProperty.ts | 65 ++++ .../syntheticImportFromBabelGeneratedFile1.ts | 20 + .../syntheticImportFromBabelGeneratedFile2.ts | 22 ++ 18 files changed, 1609 insertions(+), 24 deletions(-) create mode 100644 tests/baselines/reference/checkExportsObjectAssignProperty.errors.txt create mode 100644 tests/baselines/reference/checkExportsObjectAssignProperty.symbols create mode 100644 tests/baselines/reference/checkExportsObjectAssignProperty.types create mode 100644 tests/baselines/reference/checkObjectDefineProperty.errors.txt create mode 100644 tests/baselines/reference/checkObjectDefineProperty.symbols create mode 100644 tests/baselines/reference/checkObjectDefineProperty.types create mode 100644 tests/cases/conformance/jsdoc/checkExportsObjectAssignProperty.ts create mode 100644 tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts create mode 100644 tests/cases/fourslash/syntheticImportFromBabelGeneratedFile1.ts create mode 100644 tests/cases/fourslash/syntheticImportFromBabelGeneratedFile2.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 38d6f55824254..4a40d601a3f0f 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2112,7 +2112,7 @@ namespace ts { // Nothing to do break; default: - Debug.fail("Unknown special property assignment kind"); + Debug.fail("Unknown binary expression special property assignment kind"); } return checkStrictModeBinaryExpression(node); case SyntaxKind.CatchClause: @@ -2188,6 +2188,17 @@ namespace ts { return bindFunctionExpression(node); case SyntaxKind.CallExpression: + const assignmentKind = getAssignmentDeclarationKind(node as CallExpression); + switch (assignmentKind) { + case AssignmentDeclarationKind.ObjectDefinePropertyValue: + return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall); + case AssignmentDeclarationKind.ObjectDefinePropertyExports: + return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall); + case AssignmentDeclarationKind.None: + break; // Nothing to do + default: + return Debug.fail("Unknown call expression assignment declaration kind"); + } if (isInJSFile(node)) { bindCallExpression(node); } @@ -2361,6 +2372,22 @@ namespace ts { return true; } + function bindObjectDefinePropertyExport(node: BindableObjectDefinePropertyCall) { + if (!setCommonJsModuleIndicator(node)) { + return; + } + const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => { + if (symbol) { + addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment); + } + return symbol; + }); + if (symbol) { + const flags = SymbolFlags.Property | SymbolFlags.ExportValue; + declareSymbol(symbol.exports!, symbol, node, flags, SymbolFlags.None); + } + } + function bindExportsPropertyAssignment(node: BinaryExpression) { // When we create a property via 'exports.foo = bar', the 'exports.foo' property access // expression is the declaration @@ -2486,6 +2513,12 @@ namespace ts { bindPropertyAssignment(constructorFunction, lhs, /*isPrototypeProperty*/ true); } + function bindObjectDefinePropertyAssignment(node: BindableObjectDefinePropertyCall) { + let namespaceSymbol = lookupSymbolForPropertyAccess(node.arguments[0]); + const isToplevel = node.parent.parent.kind === SyntaxKind.SourceFile; + namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, node.arguments[0], isToplevel, /*isPrototypeProperty*/ false); + bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ false); + } function bindSpecialPropertyAssignment(node: BinaryExpression) { const lhs = node.left as PropertyAccessEntityNameExpression; @@ -2517,16 +2550,12 @@ namespace ts { bindPropertyAssignment(node.expression, node, /*isPrototypeProperty*/ false); } - function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) { - let namespaceSymbol = lookupSymbolForPropertyAccess(name); - const isToplevel = isBinaryExpression(propertyAccess.parent) - ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile - : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; + function bindPotentiallyMissingNamespaces(namespaceSymbol: Symbol | undefined, entityName: EntityNameExpression, isToplevel: boolean, isPrototypeProperty: boolean) { if (isToplevel && !isPrototypeProperty && (!namespaceSymbol || !(namespaceSymbol.flags & SymbolFlags.Namespace))) { // make symbols or add declarations for intermediate containers const flags = SymbolFlags.Module | SymbolFlags.Assignment; const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment; - namespaceSymbol = forEachIdentifierInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => { + namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => { if (symbol) { addDeclarationToSymbol(symbol, id, flags); return symbol; @@ -2538,6 +2567,10 @@ namespace ts { } }); } + return namespaceSymbol; + } + + function bindPotentiallyNewExpandoMemberToNamespace(declaration: PropertyAccessEntityNameExpression | CallExpression, namespaceSymbol: Symbol | undefined, isPrototypeProperty: boolean) { if (!namespaceSymbol || !isExpandoSymbol(namespaceSymbol)) { return; } @@ -2547,10 +2580,19 @@ namespace ts { (namespaceSymbol.members || (namespaceSymbol.members = createSymbolTable())) : (namespaceSymbol.exports || (namespaceSymbol.exports = createSymbolTable())); - const isMethod = isFunctionLikeDeclaration(getAssignedExpandoInitializer(propertyAccess)!); + const isMethod = isFunctionLikeDeclaration(getAssignedExpandoInitializer(declaration)!); const includes = isMethod ? SymbolFlags.Method : SymbolFlags.Property; const excludes = isMethod ? SymbolFlags.MethodExcludes : SymbolFlags.PropertyExcludes; - declareSymbol(symbolTable, namespaceSymbol, propertyAccess, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); + declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); + } + + function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) { + let namespaceSymbol = lookupSymbolForPropertyAccess(name); + const isToplevel = isBinaryExpression(propertyAccess.parent) + ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile + : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; + namespaceSymbol = bindPotentiallyMissingNamespaces(namespaceSymbol, propertyAccess.expression, isToplevel, isPrototypeProperty); + bindPotentiallyNewExpandoMemberToNamespace(propertyAccess, namespaceSymbol, isPrototypeProperty); } /** diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f4bc6325092de..d6a4c2634b662 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4841,7 +4841,7 @@ namespace ts { let jsdocType: Type | undefined; let types: Type[] | undefined; for (const declaration of symbol.declarations) { - const expression = isBinaryExpression(declaration) ? declaration : + const expression = (isBinaryExpression(declaration) || isCallExpression(declaration)) ? declaration : isPropertyAccessExpression(declaration) ? isBinaryExpression(declaration.parent) ? declaration.parent : declaration : undefined; if (!expression) { @@ -4857,9 +4857,11 @@ namespace ts { definedInMethod = true; } } - jsdocType = getJSDocTypeFromAssignmentDeclaration(jsdocType, expression, symbol, declaration); + if (!isCallExpression(expression)) { + jsdocType = getJSDocTypeFromAssignmentDeclaration(jsdocType, expression, symbol, declaration); + } if (!jsdocType) { - (types || (types = [])).push(isBinaryExpression(expression) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); + (types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType); } } let type = jsdocType; @@ -4922,7 +4924,32 @@ namespace ts { } /** If we don't have an explicit JSDoc type, get the type from the initializer. */ - function getInitializerTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: BinaryExpression, kind: AssignmentDeclarationKind) { + function getInitializerTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol: Symbol | undefined, expression: BinaryExpression | CallExpression, kind: AssignmentDeclarationKind) { + if (isCallExpression(expression)) { + if (resolvedSymbol) { + return getTypeOfSymbol(resolvedSymbol); // This shouldn't happen except under some hopefully forbidden merges of export assignments and object define assignments + } + const objectLitType = checkExpressionCached((expression as BindableObjectDefinePropertyCall).arguments[2]); + const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String); + if (valueType) { + return valueType; + } + const getFunc = getTypeOfPropertyOfType(objectLitType, "get" as __String); + if (getFunc) { + const getSig = getSingleCallSignature(getFunc); + if (getSig) { + return getReturnTypeOfSignature(getSig); + } + } + const setFunc = getTypeOfPropertyOfType(objectLitType, "set" as __String); + if (setFunc) { + const setSig = getSingleCallSignature(setFunc); + if (setSig) { + return getTypeOfFirstParameterOfSignature(setSig); + } + } + return anyType; + } const type = resolvedSymbol ? getTypeOfSymbol(resolvedSymbol) : getWidenedLiteralType(checkExpressionCached(expression.right)); if (type.flags & TypeFlags.Object && kind === AssignmentDeclarationKind.ModuleExports && @@ -5176,7 +5203,7 @@ namespace ts { } let type: Type | undefined; if (isInJSFile(declaration) && - (isBinaryExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent))) { + (isCallExpression(declaration) || isBinaryExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent))) { type = getWidenedTypeFromAssignmentDeclaration(symbol); } else if (isJSDocPropertyLikeTag(declaration) @@ -16535,6 +16562,9 @@ namespace ts { } const thisType = checkThisExpression(thisAccess.expression); return thisType && getTypeOfPropertyOfContextualType(thisType, thisAccess.name.escapedText) || false; + case AssignmentDeclarationKind.ObjectDefinePropertyValue: + case AssignmentDeclarationKind.ObjectDefinePropertyExports: + return Debug.fail("Unimplemented"); default: return Debug.assertNever(kind); } @@ -21206,18 +21236,52 @@ namespace ts { return true; } + function isReadonlyAssignmentDeclaration(d: Declaration) { + if (!isCallExpression(d)) { + return false; + } + if (!isBindableObjectDefinePropertyCall(d)) { + return false; + } + const objectLitType = checkExpressionCached(d.arguments[2]); + const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String); + if (valueType) { + const writableType = getTypeOfPropertyOfType(objectLitType, "writable" as __String); + if (!writableType || writableType === falseType || writableType === regularFalseType) { + return true; + } + // We include this definition whereupon we walk back and check the type at the declaration because + // The usual definition of `Object.defineProperty` will _not_ cause literal type to be preserved in the + // argument types, should the type be contextualized by the call itself. + const writableProp = getPropertyOfType(objectLitType, "writable" as __String); + if (writableProp!.valueDeclaration && isPropertyAssignment(writableProp!.valueDeclaration)) { + const initializer = (writableProp!.valueDeclaration as PropertyAssignment).initializer; + const rawOriginalType = checkExpression(initializer); + if (rawOriginalType === falseType || rawOriginalType === regularFalseType) { + return true; + } + } + return false; + } + const setProp = getPropertyOfType(objectLitType, "set" as __String); + return !setProp; + } + function isReadonlySymbol(symbol: Symbol): boolean { // The following symbols are considered read-only: // Properties with a 'readonly' modifier // Variables declared with 'const' // Get accessors without matching set accessors // Enum members + // JS Assignments with writable false or no setter // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) return !!(getCheckFlags(symbol) & CheckFlags.Readonly || symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly || symbol.flags & SymbolFlags.Variable && getDeclarationNodeFlagsFromSymbol(symbol) & NodeFlags.Const || symbol.flags & SymbolFlags.Accessor && !(symbol.flags & SymbolFlags.SetAccessor) || - symbol.flags & SymbolFlags.EnumMember); + symbol.flags & SymbolFlags.EnumMember || + some(symbol.declarations, isReadonlyAssignmentDeclaration) + ); } function isReferenceToReadonlyEntity(expr: Expression, symbol: Symbol): boolean { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index df0396b172dd4..4b8fb3fc91b51 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -713,7 +713,7 @@ namespace ts { export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; - export type DeclarationName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | BindingPattern; + export type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | BindingPattern; export interface Declaration extends Node { _declarationBrand: any; @@ -1697,6 +1697,10 @@ namespace ts { arguments: NodeArray; } + /** @internal */ + export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; + + // see: https://tc39.github.io/ecma262/#prod-SuperCall export interface SuperCall extends CallExpression { expression: SuperExpression; @@ -4252,6 +4256,13 @@ namespace ts { Property, // F.prototype = { ... } Prototype, + // Object.defineProperty(x, 'name', { value: any, writable?: boolean (false by default) }); + // Object.defineProperty(x, 'name', { get: Function, set: Function }); + // Object.defineProperty(x, 'name', { get: Function }); + // Object.defineProperty(x, 'name', { set: Function }); + ObjectDefinePropertyValue, + // Object.defineProperty(exports || module.exports, 'name', ...); + ObjectDefinePropertyExports, } /** @deprecated Use FileExtensionInfo instead. */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 00e35fb0a8588..48e4c80a453f9 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -765,12 +765,13 @@ namespace ts { return info.declaration ? declarationNameToString(info.declaration.parameters[0].name) : undefined; } - export function getTextOfPropertyName(name: PropertyName): __String { + export function getTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String { switch (name.kind) { case SyntaxKind.Identifier: return name.escapedText; case SyntaxKind.StringLiteral: case SyntaxKind.NumericLiteral: + case SyntaxKind.NoSubstitutionTemplateLiteral: return escapeLeadingUnderscores(name.text); case SyntaxKind.ComputedPropertyName: return isStringOrNumericLiteralLike(name.expression) ? escapeLeadingUnderscores(name.expression.text) : undefined!; // TODO: GH#18217 Almost all uses of this assume the result to be defined! @@ -1769,7 +1770,7 @@ namespace ts { } export function isAssignmentDeclaration(decl: Declaration) { - return isBinaryExpression(decl) || isPropertyAccessExpression(decl) || isIdentifier(decl); + return isBinaryExpression(decl) || isPropertyAccessExpression(decl) || isIdentifier(decl) || isCallExpression(decl); } /** Get the initializer, taking into account defaulted Javascript initializers */ @@ -1788,6 +1789,25 @@ namespace ts { return init && getExpandoInitializer(init, isPrototypeAccess(node.name)); } + export function getDescriptorInitializerOfKind(props: NodeArray, kind: "value" | "get" | "set") { + const init = find(props, p => !!p.name && getTextOfPropertyName(p.name) === kind); + if (init) { + switch (init.kind) { + case SyntaxKind.PropertyAssignment: + return init.initializer; + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.MethodDeclaration: + case SyntaxKind.GetAccessor: + return init; + case SyntaxKind.SetAccessor: + return undefined; + default: + return Debug.fail("Spread assignments could never have the name 'value'"); + } + } + return undefined; + } + /** * Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer). * We treat the right hand side of assignments with container-like initalizers as declarations. @@ -1904,12 +1924,32 @@ namespace ts { /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder - export function getAssignmentDeclarationKind(expr: BinaryExpression): AssignmentDeclarationKind { + export function getAssignmentDeclarationKind(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind { const special = getAssignmentDeclarationKindWorker(expr); return special === AssignmentDeclarationKind.Property || isInJSFile(expr) ? special : AssignmentDeclarationKind.None; } - function getAssignmentDeclarationKindWorker(expr: BinaryExpression): AssignmentDeclarationKind { + export function isBindableObjectDefinePropertyCall(expr: CallExpression): expr is BindableObjectDefinePropertyCall { + return !(length(expr.arguments) !== 3 || + !isPropertyAccessExpression(expr.expression) || + !isIdentifier(expr.expression.expression) || + idText(expr.expression.expression) !== "Object" || + idText(expr.expression.name) !== "defineProperty" || + !isStringOrNumericLiteralLike(expr.arguments[1]) || + !isEntityNameExpression(expr.arguments[0])); + } + + function getAssignmentDeclarationKindWorker(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind { + if (isCallExpression(expr)) { + if (!isBindableObjectDefinePropertyCall(expr)) { + return AssignmentDeclarationKind.None; + } + const entityName = expr.arguments[0]; + if ((isIdentifier(entityName) && entityName.escapedText === "exports") || (isPropertyAccessExpression(entityName) && isIdentifier(entityName.expression) && entityName.expression.escapedText === "module" && entityName.name.escapedText === "exports")) { + return AssignmentDeclarationKind.ObjectDefinePropertyExports; + } + return AssignmentDeclarationKind.ObjectDefinePropertyValue; + } if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || !isPropertyAccessExpression(expr.left)) { return AssignmentDeclarationKind.None; @@ -4976,14 +5016,18 @@ namespace ts { } break; } + case SyntaxKind.CallExpression: case SyntaxKind.BinaryExpression: { - const expr = declaration as BinaryExpression; + const expr = declaration as BinaryExpression | CallExpression; switch (getAssignmentDeclarationKind(expr)) { case AssignmentDeclarationKind.ExportsProperty: case AssignmentDeclarationKind.ThisProperty: case AssignmentDeclarationKind.Property: case AssignmentDeclarationKind.PrototypeProperty: - return (expr.left as PropertyAccessExpression).name; + return ((expr as BinaryExpression).left as PropertyAccessExpression).name; + case AssignmentDeclarationKind.ObjectDefinePropertyValue: + case AssignmentDeclarationKind.ObjectDefinePropertyExports: + return (expr as BindableObjectDefinePropertyCall).arguments[1]; default: return undefined; } diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index ab7c4327bb511..47bed08f21ca4 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -281,6 +281,8 @@ namespace ts.NavigationBar { case AssignmentDeclarationKind.ThisProperty: case AssignmentDeclarationKind.Property: case AssignmentDeclarationKind.None: + case AssignmentDeclarationKind.ObjectDefinePropertyValue: + case AssignmentDeclarationKind.ObjectDefinePropertyExports: break; default: Debug.assertNever(special); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index a2eb2f0270c53..744ebf6c8d6dd 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -368,6 +368,8 @@ namespace ts { const kind = getAssignmentDeclarationKind(node as BinaryExpression); const { right } = node as BinaryExpression; switch (kind) { + case AssignmentDeclarationKind.ObjectDefinePropertyValue: + case AssignmentDeclarationKind.ObjectDefinePropertyExports: case AssignmentDeclarationKind.None: return ScriptElementKind.unknown; case AssignmentDeclarationKind.ExportsProperty: diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e9b896e9d1714..216197c4be55c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -525,7 +525,7 @@ declare namespace ts { } type EntityName = Identifier | QualifiedName; type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; - type DeclarationName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | BindingPattern; + type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | BindingPattern; interface Declaration extends Node { _declarationBrand: any; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index cdc5017209ae5..8be97f7fc98e2 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -525,7 +525,7 @@ declare namespace ts { } type EntityName = Identifier | QualifiedName; type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName; - type DeclarationName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | BindingPattern; + type DeclarationName = Identifier | StringLiteralLike | NumericLiteral | ComputedPropertyName | BindingPattern; interface Declaration extends Node { _declarationBrand: any; } diff --git a/tests/baselines/reference/checkExportsObjectAssignProperty.errors.txt b/tests/baselines/reference/checkExportsObjectAssignProperty.errors.txt new file mode 100644 index 0000000000000..266c2180a685f --- /dev/null +++ b/tests/baselines/reference/checkExportsObjectAssignProperty.errors.txt @@ -0,0 +1,110 @@ +tests/cases/conformance/jsdoc/validator.ts(17,4): error TS2540: Cannot assign to 'readonlyProp' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validator.ts(18,4): error TS2540: Cannot assign to 'readonlyAccessor' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validator.ts(19,1): error TS2322: Type '"no"' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/validator.ts(20,1): error TS2322: Type '"no"' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/validator.ts(21,1): error TS2322: Type '0' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/validator.ts(37,4): error TS2540: Cannot assign to 'readonlyProp' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validator.ts(38,4): error TS2540: Cannot assign to 'readonlyAccessor' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validator.ts(39,1): error TS2322: Type '0' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/validator.ts(40,1): error TS2322: Type '"no"' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/validator.ts(41,1): error TS2322: Type '0' is not assignable to type 'string'. + + +==== tests/cases/conformance/jsdoc/validator.ts (10 errors) ==== + import "./"; + + import m1 = require("./mod1"); + + m1.thing; + m1.readonlyProp; + m1.rwAccessors; + m1.readonlyAccessor; + m1.setonlyAccessor; + + // allowed assignments + m1.thing = 10; + m1.rwAccessors = 11; + m1.setonlyAccessor = "yes"; + + // disallowed assignments + m1.readonlyProp = "name"; + ~~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'readonlyProp' because it is a constant or a read-only property. + m1.readonlyAccessor = 12; + ~~~~~~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'readonlyAccessor' because it is a constant or a read-only property. + m1.thing = "no"; + ~~~~~~~~ +!!! error TS2322: Type '"no"' is not assignable to type 'number'. + m1.rwAccessors = "no"; + ~~~~~~~~~~~~~~ +!!! error TS2322: Type '"no"' is not assignable to type 'number'. + m1.setonlyAccessor = 0; + ~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '0' is not assignable to type 'string'. + + import m2 = require("./mod2"); + + m2.thing; + m2.readonlyProp; + m2.rwAccessors; + m2.readonlyAccessor; + m2.setonlyAccessor; + + // allowed assignments + m2.thing = "ok"; + m2.rwAccessors = 11; + m2.setonlyAccessor = "yes"; + + // disallowed assignments + m2.readonlyProp = "name"; + ~~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'readonlyProp' because it is a constant or a read-only property. + m2.readonlyAccessor = 12; + ~~~~~~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'readonlyAccessor' because it is a constant or a read-only property. + m2.thing = 0; + ~~~~~~~~ +!!! error TS2322: Type '0' is not assignable to type 'string'. + m2.rwAccessors = "no"; + ~~~~~~~~~~~~~~ +!!! error TS2322: Type '"no"' is not assignable to type 'number'. + m2.setonlyAccessor = 0; + ~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '0' is not assignable to type 'string'. + +==== tests/cases/conformance/jsdoc/mod1.js (0 errors) ==== + Object.defineProperty(exports, "thing", { value: 42, writable: true }); + Object.defineProperty(exports, "readonlyProp", { value: "Smith", writable: false }); + Object.defineProperty(exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); + Object.defineProperty(exports, "readonlyAccessor", { get() { return 21.75 } }); + Object.defineProperty(exports, "setonlyAccessor", { + /** @param {string} str */ + set(str) { + this.rwAccessors = Number(str) + } + }); + +==== tests/cases/conformance/jsdoc/mod2.js (0 errors) ==== + Object.defineProperty(module.exports, "thing", { value: "yes", writable: true }); + Object.defineProperty(module.exports, "readonlyProp", { value: "Smith", writable: false }); + Object.defineProperty(module.exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); + Object.defineProperty(module.exports, "readonlyAccessor", { get() { return 21.75 } }); + Object.defineProperty(module.exports, "setonlyAccessor", { + /** @param {string} str */ + set(str) { + this.rwAccessors = Number(str) + } + }); + +==== tests/cases/conformance/jsdoc/index.js (0 errors) ==== + /** + * @type {number} + */ + const q = require("./mod1").thing; + + /** + * @type {string} + */ + const u = require("./mod2").thing; + \ No newline at end of file diff --git a/tests/baselines/reference/checkExportsObjectAssignProperty.symbols b/tests/baselines/reference/checkExportsObjectAssignProperty.symbols new file mode 100644 index 0000000000000..d33e2de1e209a --- /dev/null +++ b/tests/baselines/reference/checkExportsObjectAssignProperty.symbols @@ -0,0 +1,276 @@ +=== tests/cases/conformance/jsdoc/validator.ts === +import "./"; + +import m1 = require("./mod1"); +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) + +m1.thing; +>m1.thing : Symbol(m1["thing"], Decl(mod1.js, 0, 0)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>thing : Symbol(m1["thing"], Decl(mod1.js, 0, 0)) + +m1.readonlyProp; +>m1.readonlyProp : Symbol(m1["readonlyProp"], Decl(mod1.js, 0, 71)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>readonlyProp : Symbol(m1["readonlyProp"], Decl(mod1.js, 0, 71)) + +m1.rwAccessors; +>m1.rwAccessors : Symbol(m1["rwAccessors"], Decl(mod1.js, 1, 84)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>rwAccessors : Symbol(m1["rwAccessors"], Decl(mod1.js, 1, 84)) + +m1.readonlyAccessor; +>m1.readonlyAccessor : Symbol(m1["readonlyAccessor"], Decl(mod1.js, 2, 97)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>readonlyAccessor : Symbol(m1["readonlyAccessor"], Decl(mod1.js, 2, 97)) + +m1.setonlyAccessor; +>m1.setonlyAccessor : Symbol(m1["setonlyAccessor"], Decl(mod1.js, 3, 79)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>setonlyAccessor : Symbol(m1["setonlyAccessor"], Decl(mod1.js, 3, 79)) + +// allowed assignments +m1.thing = 10; +>m1.thing : Symbol(m1["thing"], Decl(mod1.js, 0, 0)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>thing : Symbol(m1["thing"], Decl(mod1.js, 0, 0)) + +m1.rwAccessors = 11; +>m1.rwAccessors : Symbol(m1["rwAccessors"], Decl(mod1.js, 1, 84)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>rwAccessors : Symbol(m1["rwAccessors"], Decl(mod1.js, 1, 84)) + +m1.setonlyAccessor = "yes"; +>m1.setonlyAccessor : Symbol(m1["setonlyAccessor"], Decl(mod1.js, 3, 79)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>setonlyAccessor : Symbol(m1["setonlyAccessor"], Decl(mod1.js, 3, 79)) + +// disallowed assignments +m1.readonlyProp = "name"; +>m1.readonlyProp : Symbol(m1["readonlyProp"], Decl(mod1.js, 0, 71)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>readonlyProp : Symbol(m1["readonlyProp"], Decl(mod1.js, 0, 71)) + +m1.readonlyAccessor = 12; +>m1.readonlyAccessor : Symbol(m1["readonlyAccessor"], Decl(mod1.js, 2, 97)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>readonlyAccessor : Symbol(m1["readonlyAccessor"], Decl(mod1.js, 2, 97)) + +m1.thing = "no"; +>m1.thing : Symbol(m1["thing"], Decl(mod1.js, 0, 0)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>thing : Symbol(m1["thing"], Decl(mod1.js, 0, 0)) + +m1.rwAccessors = "no"; +>m1.rwAccessors : Symbol(m1["rwAccessors"], Decl(mod1.js, 1, 84)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>rwAccessors : Symbol(m1["rwAccessors"], Decl(mod1.js, 1, 84)) + +m1.setonlyAccessor = 0; +>m1.setonlyAccessor : Symbol(m1["setonlyAccessor"], Decl(mod1.js, 3, 79)) +>m1 : Symbol(m1, Decl(validator.ts, 0, 12)) +>setonlyAccessor : Symbol(m1["setonlyAccessor"], Decl(mod1.js, 3, 79)) + +import m2 = require("./mod2"); +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) + +m2.thing; +>m2.thing : Symbol(m2["thing"], Decl(mod2.js, 0, 0)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>thing : Symbol(m2["thing"], Decl(mod2.js, 0, 0)) + +m2.readonlyProp; +>m2.readonlyProp : Symbol(m2["readonlyProp"], Decl(mod2.js, 0, 81)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>readonlyProp : Symbol(m2["readonlyProp"], Decl(mod2.js, 0, 81)) + +m2.rwAccessors; +>m2.rwAccessors : Symbol(m2["rwAccessors"], Decl(mod2.js, 1, 91)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>rwAccessors : Symbol(m2["rwAccessors"], Decl(mod2.js, 1, 91)) + +m2.readonlyAccessor; +>m2.readonlyAccessor : Symbol(m2["readonlyAccessor"], Decl(mod2.js, 2, 104)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>readonlyAccessor : Symbol(m2["readonlyAccessor"], Decl(mod2.js, 2, 104)) + +m2.setonlyAccessor; +>m2.setonlyAccessor : Symbol(m2["setonlyAccessor"], Decl(mod2.js, 3, 86)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>setonlyAccessor : Symbol(m2["setonlyAccessor"], Decl(mod2.js, 3, 86)) + +// allowed assignments +m2.thing = "ok"; +>m2.thing : Symbol(m2["thing"], Decl(mod2.js, 0, 0)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>thing : Symbol(m2["thing"], Decl(mod2.js, 0, 0)) + +m2.rwAccessors = 11; +>m2.rwAccessors : Symbol(m2["rwAccessors"], Decl(mod2.js, 1, 91)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>rwAccessors : Symbol(m2["rwAccessors"], Decl(mod2.js, 1, 91)) + +m2.setonlyAccessor = "yes"; +>m2.setonlyAccessor : Symbol(m2["setonlyAccessor"], Decl(mod2.js, 3, 86)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>setonlyAccessor : Symbol(m2["setonlyAccessor"], Decl(mod2.js, 3, 86)) + +// disallowed assignments +m2.readonlyProp = "name"; +>m2.readonlyProp : Symbol(m2["readonlyProp"], Decl(mod2.js, 0, 81)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>readonlyProp : Symbol(m2["readonlyProp"], Decl(mod2.js, 0, 81)) + +m2.readonlyAccessor = 12; +>m2.readonlyAccessor : Symbol(m2["readonlyAccessor"], Decl(mod2.js, 2, 104)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>readonlyAccessor : Symbol(m2["readonlyAccessor"], Decl(mod2.js, 2, 104)) + +m2.thing = 0; +>m2.thing : Symbol(m2["thing"], Decl(mod2.js, 0, 0)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>thing : Symbol(m2["thing"], Decl(mod2.js, 0, 0)) + +m2.rwAccessors = "no"; +>m2.rwAccessors : Symbol(m2["rwAccessors"], Decl(mod2.js, 1, 91)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>rwAccessors : Symbol(m2["rwAccessors"], Decl(mod2.js, 1, 91)) + +m2.setonlyAccessor = 0; +>m2.setonlyAccessor : Symbol(m2["setonlyAccessor"], Decl(mod2.js, 3, 86)) +>m2 : Symbol(m2, Decl(validator.ts, 20, 23)) +>setonlyAccessor : Symbol(m2["setonlyAccessor"], Decl(mod2.js, 3, 86)) + +=== tests/cases/conformance/jsdoc/mod1.js === +Object.defineProperty(exports, "thing", { value: 42, writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>value : Symbol(value, Decl(mod1.js, 0, 41)) +>writable : Symbol(writable, Decl(mod1.js, 0, 52)) + +Object.defineProperty(exports, "readonlyProp", { value: "Smith", writable: false }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>value : Symbol(value, Decl(mod1.js, 1, 48)) +>writable : Symbol(writable, Decl(mod1.js, 1, 64)) + +Object.defineProperty(exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>get : Symbol(get, Decl(mod1.js, 2, 47)) +>set : Symbol(set, Decl(mod1.js, 2, 71)) +>_ : Symbol(_, Decl(mod1.js, 2, 76)) + +Object.defineProperty(exports, "readonlyAccessor", { get() { return 21.75 } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>get : Symbol(get, Decl(mod1.js, 3, 52)) + +Object.defineProperty(exports, "setonlyAccessor", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) + + /** @param {string} str */ + set(str) { +>set : Symbol(set, Decl(mod1.js, 4, 51)) +>str : Symbol(str, Decl(mod1.js, 6, 8)) + + this.rwAccessors = Number(str) +>rwAccessors : Symbol(rwAccessors, Decl(mod1.js, 6, 14)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>str : Symbol(str, Decl(mod1.js, 6, 8)) + } +}); + +=== tests/cases/conformance/jsdoc/mod2.js === +Object.defineProperty(module.exports, "thing", { value: "yes", writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>module : Symbol(module, Decl(mod2.js, 0, 22)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>value : Symbol(value, Decl(mod2.js, 0, 48)) +>writable : Symbol(writable, Decl(mod2.js, 0, 62)) + +Object.defineProperty(module.exports, "readonlyProp", { value: "Smith", writable: false }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>module : Symbol(module, Decl(mod2.js, 0, 22)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>value : Symbol(value, Decl(mod2.js, 1, 55)) +>writable : Symbol(writable, Decl(mod2.js, 1, 71)) + +Object.defineProperty(module.exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>module : Symbol(module, Decl(mod2.js, 0, 22)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>get : Symbol(get, Decl(mod2.js, 2, 54)) +>set : Symbol(set, Decl(mod2.js, 2, 78)) +>_ : Symbol(_, Decl(mod2.js, 2, 83)) + +Object.defineProperty(module.exports, "readonlyAccessor", { get() { return 21.75 } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>module : Symbol(module, Decl(mod2.js, 0, 22)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>get : Symbol(get, Decl(mod2.js, 3, 59)) + +Object.defineProperty(module.exports, "setonlyAccessor", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>module : Symbol(module, Decl(mod2.js, 0, 22)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) + + /** @param {string} str */ + set(str) { +>set : Symbol(set, Decl(mod2.js, 4, 58)) +>str : Symbol(str, Decl(mod2.js, 6, 8)) + + this.rwAccessors = Number(str) +>rwAccessors : Symbol(rwAccessors, Decl(mod2.js, 6, 14)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>str : Symbol(str, Decl(mod2.js, 6, 8)) + } +}); + +=== tests/cases/conformance/jsdoc/index.js === +/** + * @type {number} + */ +const q = require("./mod1").thing; +>q : Symbol(q, Decl(index.js, 3, 5)) +>require("./mod1").thing : Symbol("thing", Decl(mod1.js, 0, 0)) +>require : Symbol(require) +>"./mod1" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>thing : Symbol("thing", Decl(mod1.js, 0, 0)) + +/** + * @type {string} + */ +const u = require("./mod2").thing; +>u : Symbol(u, Decl(index.js, 8, 5)) +>require("./mod2").thing : Symbol("thing", Decl(mod2.js, 0, 0)) +>require : Symbol(require) +>"./mod2" : Symbol("tests/cases/conformance/jsdoc/mod2", Decl(mod2.js, 0, 0)) +>thing : Symbol("thing", Decl(mod2.js, 0, 0)) + diff --git a/tests/baselines/reference/checkExportsObjectAssignProperty.types b/tests/baselines/reference/checkExportsObjectAssignProperty.types new file mode 100644 index 0000000000000..847ac720d75c1 --- /dev/null +++ b/tests/baselines/reference/checkExportsObjectAssignProperty.types @@ -0,0 +1,360 @@ +=== tests/cases/conformance/jsdoc/validator.ts === +import "./"; + +import m1 = require("./mod1"); +>m1 : typeof m1 + +m1.thing; +>m1.thing : number +>m1 : typeof m1 +>thing : number + +m1.readonlyProp; +>m1.readonlyProp : string +>m1 : typeof m1 +>readonlyProp : string + +m1.rwAccessors; +>m1.rwAccessors : number +>m1 : typeof m1 +>rwAccessors : number + +m1.readonlyAccessor; +>m1.readonlyAccessor : number +>m1 : typeof m1 +>readonlyAccessor : number + +m1.setonlyAccessor; +>m1.setonlyAccessor : string +>m1 : typeof m1 +>setonlyAccessor : string + +// allowed assignments +m1.thing = 10; +>m1.thing = 10 : 10 +>m1.thing : number +>m1 : typeof m1 +>thing : number +>10 : 10 + +m1.rwAccessors = 11; +>m1.rwAccessors = 11 : 11 +>m1.rwAccessors : number +>m1 : typeof m1 +>rwAccessors : number +>11 : 11 + +m1.setonlyAccessor = "yes"; +>m1.setonlyAccessor = "yes" : "yes" +>m1.setonlyAccessor : string +>m1 : typeof m1 +>setonlyAccessor : string +>"yes" : "yes" + +// disallowed assignments +m1.readonlyProp = "name"; +>m1.readonlyProp = "name" : "name" +>m1.readonlyProp : any +>m1 : typeof m1 +>readonlyProp : any +>"name" : "name" + +m1.readonlyAccessor = 12; +>m1.readonlyAccessor = 12 : 12 +>m1.readonlyAccessor : any +>m1 : typeof m1 +>readonlyAccessor : any +>12 : 12 + +m1.thing = "no"; +>m1.thing = "no" : "no" +>m1.thing : number +>m1 : typeof m1 +>thing : number +>"no" : "no" + +m1.rwAccessors = "no"; +>m1.rwAccessors = "no" : "no" +>m1.rwAccessors : number +>m1 : typeof m1 +>rwAccessors : number +>"no" : "no" + +m1.setonlyAccessor = 0; +>m1.setonlyAccessor = 0 : 0 +>m1.setonlyAccessor : string +>m1 : typeof m1 +>setonlyAccessor : string +>0 : 0 + +import m2 = require("./mod2"); +>m2 : typeof m2 + +m2.thing; +>m2.thing : string +>m2 : typeof m2 +>thing : string + +m2.readonlyProp; +>m2.readonlyProp : string +>m2 : typeof m2 +>readonlyProp : string + +m2.rwAccessors; +>m2.rwAccessors : number +>m2 : typeof m2 +>rwAccessors : number + +m2.readonlyAccessor; +>m2.readonlyAccessor : number +>m2 : typeof m2 +>readonlyAccessor : number + +m2.setonlyAccessor; +>m2.setonlyAccessor : string +>m2 : typeof m2 +>setonlyAccessor : string + +// allowed assignments +m2.thing = "ok"; +>m2.thing = "ok" : "ok" +>m2.thing : string +>m2 : typeof m2 +>thing : string +>"ok" : "ok" + +m2.rwAccessors = 11; +>m2.rwAccessors = 11 : 11 +>m2.rwAccessors : number +>m2 : typeof m2 +>rwAccessors : number +>11 : 11 + +m2.setonlyAccessor = "yes"; +>m2.setonlyAccessor = "yes" : "yes" +>m2.setonlyAccessor : string +>m2 : typeof m2 +>setonlyAccessor : string +>"yes" : "yes" + +// disallowed assignments +m2.readonlyProp = "name"; +>m2.readonlyProp = "name" : "name" +>m2.readonlyProp : any +>m2 : typeof m2 +>readonlyProp : any +>"name" : "name" + +m2.readonlyAccessor = 12; +>m2.readonlyAccessor = 12 : 12 +>m2.readonlyAccessor : any +>m2 : typeof m2 +>readonlyAccessor : any +>12 : 12 + +m2.thing = 0; +>m2.thing = 0 : 0 +>m2.thing : string +>m2 : typeof m2 +>thing : string +>0 : 0 + +m2.rwAccessors = "no"; +>m2.rwAccessors = "no" : "no" +>m2.rwAccessors : number +>m2 : typeof m2 +>rwAccessors : number +>"no" : "no" + +m2.setonlyAccessor = 0; +>m2.setonlyAccessor = 0 : 0 +>m2.setonlyAccessor : string +>m2 : typeof m2 +>setonlyAccessor : string +>0 : 0 + +=== tests/cases/conformance/jsdoc/mod1.js === +Object.defineProperty(exports, "thing", { value: 42, writable: true }); +>Object.defineProperty(exports, "thing", { value: 42, writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"thing" : "thing" +>{ value: 42, writable: true } : { value: number; writable: true; } +>value : number +>42 : 42 +>writable : true +>true : true + +Object.defineProperty(exports, "readonlyProp", { value: "Smith", writable: false }); +>Object.defineProperty(exports, "readonlyProp", { value: "Smith", writable: false }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"readonlyProp" : "readonlyProp" +>{ value: "Smith", writable: false } : { value: string; writable: false; } +>value : string +>"Smith" : "Smith" +>writable : false +>false : false + +Object.defineProperty(exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty(exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"rwAccessors" : "rwAccessors" +>{ get() { return 98122 }, set(_) { /*ignore*/ } } : { get(): number; set(_: any): void; } +>get : () => number +>98122 : 98122 +>set : (_: any) => void +>_ : any + +Object.defineProperty(exports, "readonlyAccessor", { get() { return 21.75 } }); +>Object.defineProperty(exports, "readonlyAccessor", { get() { return 21.75 } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"readonlyAccessor" : "readonlyAccessor" +>{ get() { return 21.75 } } : { get(): number; } +>get : () => number +>21.75 : 21.75 + +Object.defineProperty(exports, "setonlyAccessor", { +>Object.defineProperty(exports, "setonlyAccessor", { /** @param {string} str */ set(str) { this.rwAccessors = Number(str) }}) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"setonlyAccessor" : "setonlyAccessor" +>{ /** @param {string} str */ set(str) { this.rwAccessors = Number(str) }} : { set(str: string): void; } + + /** @param {string} str */ + set(str) { +>set : (str: string) => void +>str : string + + this.rwAccessors = Number(str) +>this.rwAccessors = Number(str) : number +>this.rwAccessors : any +>this : any +>rwAccessors : any +>Number(str) : number +>Number : NumberConstructor +>str : string + } +}); + +=== tests/cases/conformance/jsdoc/mod2.js === +Object.defineProperty(module.exports, "thing", { value: "yes", writable: true }); +>Object.defineProperty(module.exports, "thing", { value: "yes", writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>module.exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>module : { "tests/cases/conformance/jsdoc/mod2": typeof import("tests/cases/conformance/jsdoc/mod2"); } +>exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>"thing" : "thing" +>{ value: "yes", writable: true } : { value: string; writable: true; } +>value : string +>"yes" : "yes" +>writable : true +>true : true + +Object.defineProperty(module.exports, "readonlyProp", { value: "Smith", writable: false }); +>Object.defineProperty(module.exports, "readonlyProp", { value: "Smith", writable: false }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>module.exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>module : { "tests/cases/conformance/jsdoc/mod2": typeof import("tests/cases/conformance/jsdoc/mod2"); } +>exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>"readonlyProp" : "readonlyProp" +>{ value: "Smith", writable: false } : { value: string; writable: false; } +>value : string +>"Smith" : "Smith" +>writable : false +>false : false + +Object.defineProperty(module.exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty(module.exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>module.exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>module : { "tests/cases/conformance/jsdoc/mod2": typeof import("tests/cases/conformance/jsdoc/mod2"); } +>exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>"rwAccessors" : "rwAccessors" +>{ get() { return 98122 }, set(_) { /*ignore*/ } } : { get(): number; set(_: any): void; } +>get : () => number +>98122 : 98122 +>set : (_: any) => void +>_ : any + +Object.defineProperty(module.exports, "readonlyAccessor", { get() { return 21.75 } }); +>Object.defineProperty(module.exports, "readonlyAccessor", { get() { return 21.75 } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>module.exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>module : { "tests/cases/conformance/jsdoc/mod2": typeof import("tests/cases/conformance/jsdoc/mod2"); } +>exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>"readonlyAccessor" : "readonlyAccessor" +>{ get() { return 21.75 } } : { get(): number; } +>get : () => number +>21.75 : 21.75 + +Object.defineProperty(module.exports, "setonlyAccessor", { +>Object.defineProperty(module.exports, "setonlyAccessor", { /** @param {string} str */ set(str) { this.rwAccessors = Number(str) }}) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>module.exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>module : { "tests/cases/conformance/jsdoc/mod2": typeof import("tests/cases/conformance/jsdoc/mod2"); } +>exports : typeof import("tests/cases/conformance/jsdoc/mod2") +>"setonlyAccessor" : "setonlyAccessor" +>{ /** @param {string} str */ set(str) { this.rwAccessors = Number(str) }} : { set(str: string): void; } + + /** @param {string} str */ + set(str) { +>set : (str: string) => void +>str : string + + this.rwAccessors = Number(str) +>this.rwAccessors = Number(str) : number +>this.rwAccessors : any +>this : any +>rwAccessors : any +>Number(str) : number +>Number : NumberConstructor +>str : string + } +}); + +=== tests/cases/conformance/jsdoc/index.js === +/** + * @type {number} + */ +const q = require("./mod1").thing; +>q : number +>require("./mod1").thing : number +>require("./mod1") : typeof import("tests/cases/conformance/jsdoc/mod1") +>require : any +>"./mod1" : "./mod1" +>thing : number + +/** + * @type {string} + */ +const u = require("./mod2").thing; +>u : string +>require("./mod2").thing : string +>require("./mod2") : typeof import("tests/cases/conformance/jsdoc/mod2") +>require : any +>"./mod2" : "./mod2" +>thing : string + diff --git a/tests/baselines/reference/checkObjectDefineProperty.errors.txt b/tests/baselines/reference/checkObjectDefineProperty.errors.txt new file mode 100644 index 0000000000000..44ffcd723e9bb --- /dev/null +++ b/tests/baselines/reference/checkObjectDefineProperty.errors.txt @@ -0,0 +1,73 @@ +tests/cases/conformance/jsdoc/validate.ts(13,3): error TS2540: Cannot assign to 'lastName' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validate.ts(14,3): error TS2540: Cannot assign to 'houseNumber' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validate.ts(15,1): error TS2322: Type '12' is not assignable to type 'string'. + + +==== tests/cases/conformance/jsdoc/validate.ts (3 errors) ==== + // Validate in TS as simple validations would usually be interpreted as more special assignments + import x = require("./"); + x.name; + x.lastName; + x.zip; + x.houseNumber; + x.zipStr; + + x.name = "Another"; + x.zip = 98123; + x.zipStr = "OK"; + + x.lastName = "should fail"; + ~~~~~~~~ +!!! error TS2540: Cannot assign to 'lastName' because it is a constant or a read-only property. + x.houseNumber = 12; // should also fail + ~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'houseNumber' because it is a constant or a read-only property. + x.zipStr = 12; // should fail + ~~~~~~~~ +!!! error TS2322: Type '12' is not assignable to type 'string'. + +==== tests/cases/conformance/jsdoc/index.js (0 errors) ==== + const x = {}; + Object.defineProperty(x, "name", { value: "Charles", writable: true }); + Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); + Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); + Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); + Object.defineProperty(x, "zipStr", { + /** @param {string} str */ + set(str) { + this.zip = Number(str) + } + }); + + /** + * @param {{name: string}} named + */ + function takeName(named) { return named.name; } + + takeName(x); + /** + * @type {number} + */ + var a = x.zip; + + /** + * @type {number} + */ + var b = x.houseNumber; + + const returnExemplar = () => x; + const needsExemplar = (_ = x) => void 0; + + const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); + + /** + * + * @param {typeof returnExemplar} a + * @param {typeof needsExemplar} b + */ + function match(a, b) {} + + match(() => expected, (x = expected) => void 0); + + module.exports = x; + \ No newline at end of file diff --git a/tests/baselines/reference/checkObjectDefineProperty.symbols b/tests/baselines/reference/checkObjectDefineProperty.symbols new file mode 100644 index 0000000000000..96973d3653402 --- /dev/null +++ b/tests/baselines/reference/checkObjectDefineProperty.symbols @@ -0,0 +1,180 @@ +=== tests/cases/conformance/jsdoc/validate.ts === +// Validate in TS as simple validations would usually be interpreted as more special assignments +import x = require("./"); +>x : Symbol(x, Decl(validate.ts, 0, 0)) + +x.name; +>x.name : Symbol(x["name"], Decl(index.js, 0, 13)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>name : Symbol(x["name"], Decl(index.js, 0, 13)) + +x.lastName; +>x.lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) + +x.zip; +>x.zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>zip : Symbol(x["zip"], Decl(index.js, 2, 74)) + +x.houseNumber; +>x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) + +x.zipStr; +>x.zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) + +x.name = "Another"; +>x.name : Symbol(x["name"], Decl(index.js, 0, 13)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>name : Symbol(x["name"], Decl(index.js, 0, 13)) + +x.zip = 98123; +>x.zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>zip : Symbol(x["zip"], Decl(index.js, 2, 74)) + +x.zipStr = "OK"; +>x.zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) + +x.lastName = "should fail"; +>x.lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) + +x.houseNumber = 12; // should also fail +>x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) + +x.zipStr = 12; // should fail +>x.zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) + +=== tests/cases/conformance/jsdoc/index.js === +const x = {}; +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) + +Object.defineProperty(x, "name", { value: "Charles", writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>value : Symbol(value, Decl(index.js, 1, 34)) +>writable : Symbol(writable, Decl(index.js, 1, 52)) + +Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>value : Symbol(value, Decl(index.js, 2, 38)) +>writable : Symbol(writable, Decl(index.js, 2, 54)) + +Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>get : Symbol(get, Decl(index.js, 3, 33)) +>set : Symbol(set, Decl(index.js, 3, 57)) +>_ : Symbol(_, Decl(index.js, 3, 62)) + +Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>get : Symbol(get, Decl(index.js, 4, 41)) + +Object.defineProperty(x, "zipStr", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) + + /** @param {string} str */ + set(str) { +>set : Symbol(set, Decl(index.js, 5, 36)) +>str : Symbol(str, Decl(index.js, 7, 8)) + + this.zip = Number(str) +>zip : Symbol(zip, Decl(index.js, 7, 14)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>str : Symbol(str, Decl(index.js, 7, 8)) + } +}); + +/** + * @param {{name: string}} named + */ +function takeName(named) { return named.name; } +>takeName : Symbol(takeName, Decl(index.js, 10, 3)) +>named : Symbol(named, Decl(index.js, 15, 18)) +>named.name : Symbol(name, Decl(index.js, 13, 12)) +>named : Symbol(named, Decl(index.js, 15, 18)) +>name : Symbol(name, Decl(index.js, 13, 12)) + +takeName(x); +>takeName : Symbol(takeName, Decl(index.js, 10, 3)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) + +/** + * @type {number} + */ +var a = x.zip; +>a : Symbol(a, Decl(index.js, 21, 3)) +>x.zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>zip : Symbol(x["zip"], Decl(index.js, 2, 74)) + +/** + * @type {number} + */ +var b = x.houseNumber; +>b : Symbol(b, Decl(index.js, 26, 3)) +>x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) + +const returnExemplar = () => x; +>returnExemplar : Symbol(returnExemplar, Decl(index.js, 28, 5)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) + +const needsExemplar = (_ = x) => void 0; +>needsExemplar : Symbol(needsExemplar, Decl(index.js, 29, 5)) +>_ : Symbol(_, Decl(index.js, 29, 23)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) + +const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); +>expected : Symbol(expected, Decl(index.js, 31, 5)) + +/** + * + * @param {typeof returnExemplar} a + * @param {typeof needsExemplar} b + */ +function match(a, b) {} +>match : Symbol(match, Decl(index.js, 31, 157)) +>a : Symbol(a, Decl(index.js, 38, 15)) +>b : Symbol(b, Decl(index.js, 38, 17)) + +match(() => expected, (x = expected) => void 0); +>match : Symbol(match, Decl(index.js, 31, 157)) +>expected : Symbol(expected, Decl(index.js, 31, 5)) +>x : Symbol(x, Decl(index.js, 40, 23)) +>expected : Symbol(expected, Decl(index.js, 31, 5)) + +module.exports = x; +>module.exports : Symbol("tests/cases/conformance/jsdoc/index", Decl(index.js, 0, 0)) +>module : Symbol(export=, Decl(index.js, 40, 48)) +>exports : Symbol(export=, Decl(index.js, 40, 48)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) + diff --git a/tests/baselines/reference/checkObjectDefineProperty.types b/tests/baselines/reference/checkObjectDefineProperty.types new file mode 100644 index 0000000000000..791024a3a5325 --- /dev/null +++ b/tests/baselines/reference/checkObjectDefineProperty.types @@ -0,0 +1,232 @@ +=== tests/cases/conformance/jsdoc/validate.ts === +// Validate in TS as simple validations would usually be interpreted as more special assignments +import x = require("./"); +>x : typeof x + +x.name; +>x.name : string +>x : typeof x +>name : string + +x.lastName; +>x.lastName : string +>x : typeof x +>lastName : string + +x.zip; +>x.zip : number +>x : typeof x +>zip : number + +x.houseNumber; +>x.houseNumber : number +>x : typeof x +>houseNumber : number + +x.zipStr; +>x.zipStr : string +>x : typeof x +>zipStr : string + +x.name = "Another"; +>x.name = "Another" : "Another" +>x.name : string +>x : typeof x +>name : string +>"Another" : "Another" + +x.zip = 98123; +>x.zip = 98123 : 98123 +>x.zip : number +>x : typeof x +>zip : number +>98123 : 98123 + +x.zipStr = "OK"; +>x.zipStr = "OK" : "OK" +>x.zipStr : string +>x : typeof x +>zipStr : string +>"OK" : "OK" + +x.lastName = "should fail"; +>x.lastName = "should fail" : "should fail" +>x.lastName : any +>x : typeof x +>lastName : any +>"should fail" : "should fail" + +x.houseNumber = 12; // should also fail +>x.houseNumber = 12 : 12 +>x.houseNumber : any +>x : typeof x +>houseNumber : any +>12 : 12 + +x.zipStr = 12; // should fail +>x.zipStr = 12 : 12 +>x.zipStr : string +>x : typeof x +>zipStr : string +>12 : 12 + +=== tests/cases/conformance/jsdoc/index.js === +const x = {}; +>x : typeof x +>{} : {} + +Object.defineProperty(x, "name", { value: "Charles", writable: true }); +>Object.defineProperty(x, "name", { value: "Charles", writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>x : typeof x +>"name" : "name" +>{ value: "Charles", writable: true } : { value: string; writable: true; } +>value : string +>"Charles" : "Charles" +>writable : true +>true : true + +Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); +>Object.defineProperty(x, "lastName", { value: "Smith", writable: false }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>x : typeof x +>"lastName" : "lastName" +>{ value: "Smith", writable: false } : { value: string; writable: false; } +>value : string +>"Smith" : "Smith" +>writable : false +>false : false + +Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>x : typeof x +>"zip" : "zip" +>{ get() { return 98122 }, set(_) { /*ignore*/ } } : { get(): number; set(_: any): void; } +>get : () => number +>98122 : 98122 +>set : (_: any) => void +>_ : any + +Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); +>Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>x : typeof x +>"houseNumber" : "houseNumber" +>{ get() { return 21.75 } } : { get(): number; } +>get : () => number +>21.75 : 21.75 + +Object.defineProperty(x, "zipStr", { +>Object.defineProperty(x, "zipStr", { /** @param {string} str */ set(str) { this.zip = Number(str) }}) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>x : typeof x +>"zipStr" : "zipStr" +>{ /** @param {string} str */ set(str) { this.zip = Number(str) }} : { set(str: string): void; } + + /** @param {string} str */ + set(str) { +>set : (str: string) => void +>str : string + + this.zip = Number(str) +>this.zip = Number(str) : number +>this.zip : any +>this : any +>zip : any +>Number(str) : number +>Number : NumberConstructor +>str : string + } +}); + +/** + * @param {{name: string}} named + */ +function takeName(named) { return named.name; } +>takeName : (named: { name: string; }) => string +>named : { name: string; } +>named.name : string +>named : { name: string; } +>name : string + +takeName(x); +>takeName(x) : string +>takeName : (named: { name: string; }) => string +>x : typeof x + +/** + * @type {number} + */ +var a = x.zip; +>a : number +>x.zip : number +>x : typeof x +>zip : number + +/** + * @type {number} + */ +var b = x.houseNumber; +>b : number +>x.houseNumber : number +>x : typeof x +>houseNumber : number + +const returnExemplar = () => x; +>returnExemplar : () => typeof x +>() => x : () => typeof x +>x : typeof x + +const needsExemplar = (_ = x) => void 0; +>needsExemplar : (_?: typeof x) => undefined +>(_ = x) => void 0 : (_?: typeof x) => undefined +>_ : typeof x +>x : typeof x +>void 0 : undefined +>0 : 0 + +const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); +>expected : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>(/** @type {*} */(null)) : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>(null) : any +>null : null + +/** + * + * @param {typeof returnExemplar} a + * @param {typeof needsExemplar} b + */ +function match(a, b) {} +>match : (a: () => typeof x, b: (_?: typeof x) => undefined) => void +>a : () => typeof x +>b : (_?: typeof x) => undefined + +match(() => expected, (x = expected) => void 0); +>match(() => expected, (x = expected) => void 0) : void +>match : (a: () => typeof x, b: (_?: typeof x) => undefined) => void +>() => expected : () => { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>expected : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>(x = expected) => void 0 : (x?: typeof x | undefined) => undefined +>x : typeof x | undefined +>expected : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>void 0 : undefined +>0 : 0 + +module.exports = x; +>module.exports = x : typeof x +>module.exports : typeof x +>module : { "tests/cases/conformance/jsdoc/index": typeof x; } +>exports : typeof x +>x : typeof x + diff --git a/tests/cases/conformance/jsdoc/checkExportsObjectAssignProperty.ts b/tests/cases/conformance/jsdoc/checkExportsObjectAssignProperty.ts new file mode 100644 index 0000000000000..132fef0451028 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkExportsObjectAssignProperty.ts @@ -0,0 +1,82 @@ +// @allowJs: true +// @noEmit: true +// @strict: true +// @checkJs: true +// @filename: mod1.js +Object.defineProperty(exports, "thing", { value: 42, writable: true }); +Object.defineProperty(exports, "readonlyProp", { value: "Smith", writable: false }); +Object.defineProperty(exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +Object.defineProperty(exports, "readonlyAccessor", { get() { return 21.75 } }); +Object.defineProperty(exports, "setonlyAccessor", { + /** @param {string} str */ + set(str) { + this.rwAccessors = Number(str) + } +}); + +// @filename: mod2.js +Object.defineProperty(module.exports, "thing", { value: "yes", writable: true }); +Object.defineProperty(module.exports, "readonlyProp", { value: "Smith", writable: false }); +Object.defineProperty(module.exports, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +Object.defineProperty(module.exports, "readonlyAccessor", { get() { return 21.75 } }); +Object.defineProperty(module.exports, "setonlyAccessor", { + /** @param {string} str */ + set(str) { + this.rwAccessors = Number(str) + } +}); + +// @filename: index.js + +/** + * @type {number} + */ +const q = require("./mod1").thing; + +/** + * @type {string} + */ +const u = require("./mod2").thing; + +// @filename: validator.ts +import "./"; + +import m1 = require("./mod1"); + +m1.thing; +m1.readonlyProp; +m1.rwAccessors; +m1.readonlyAccessor; +m1.setonlyAccessor; + +// allowed assignments +m1.thing = 10; +m1.rwAccessors = 11; +m1.setonlyAccessor = "yes"; + +// disallowed assignments +m1.readonlyProp = "name"; +m1.readonlyAccessor = 12; +m1.thing = "no"; +m1.rwAccessors = "no"; +m1.setonlyAccessor = 0; + +import m2 = require("./mod2"); + +m2.thing; +m2.readonlyProp; +m2.rwAccessors; +m2.readonlyAccessor; +m2.setonlyAccessor; + +// allowed assignments +m2.thing = "ok"; +m2.rwAccessors = 11; +m2.setonlyAccessor = "yes"; + +// disallowed assignments +m2.readonlyProp = "name"; +m2.readonlyAccessor = 12; +m2.thing = 0; +m2.rwAccessors = "no"; +m2.setonlyAccessor = 0; diff --git a/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts b/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts new file mode 100644 index 0000000000000..f62a34e1c64a8 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts @@ -0,0 +1,65 @@ +// @allowJs: true +// @noEmit: true +// @strict: true +// @checkJs: true +// @filename: index.js +const x = {}; +Object.defineProperty(x, "name", { value: "Charles", writable: true }); +Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); +Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); +Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); +Object.defineProperty(x, "zipStr", { + /** @param {string} str */ + set(str) { + this.zip = Number(str) + } +}); + +/** + * @param {{name: string}} named + */ +function takeName(named) { return named.name; } + +takeName(x); +/** + * @type {number} + */ +var a = x.zip; + +/** + * @type {number} + */ +var b = x.houseNumber; + +const returnExemplar = () => x; +const needsExemplar = (_ = x) => void 0; + +const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); + +/** + * + * @param {typeof returnExemplar} a + * @param {typeof needsExemplar} b + */ +function match(a, b) {} + +match(() => expected, (x = expected) => void 0); + +module.exports = x; + +// @filename: validate.ts +// Validate in TS as simple validations would usually be interpreted as more special assignments +import x = require("./"); +x.name; +x.lastName; +x.zip; +x.houseNumber; +x.zipStr; + +x.name = "Another"; +x.zip = 98123; +x.zipStr = "OK"; + +x.lastName = "should fail"; +x.houseNumber = 12; // should also fail +x.zipStr = 12; // should fail diff --git a/tests/cases/fourslash/syntheticImportFromBabelGeneratedFile1.ts b/tests/cases/fourslash/syntheticImportFromBabelGeneratedFile1.ts new file mode 100644 index 0000000000000..4dd0654a897df --- /dev/null +++ b/tests/cases/fourslash/syntheticImportFromBabelGeneratedFile1.ts @@ -0,0 +1,20 @@ +/// + +// @allowJs: true +// @allowSyntheticDefaultImports: true + +// @Filename: /a.js +////exports.__esModule = true; +////exports.default = f; +/////** +//// * Run this function +//// * @param {string} t +//// */ +////function f(t) {} + +// @Filename: /b.js +////import f from "./a" +/////**/f + +verify.quickInfoAt("", `(alias) (property) f: (t: string) => void +import f`, "Run this function"); // Passes \ No newline at end of file diff --git a/tests/cases/fourslash/syntheticImportFromBabelGeneratedFile2.ts b/tests/cases/fourslash/syntheticImportFromBabelGeneratedFile2.ts new file mode 100644 index 0000000000000..0cfb4e684542c --- /dev/null +++ b/tests/cases/fourslash/syntheticImportFromBabelGeneratedFile2.ts @@ -0,0 +1,22 @@ +/// + +// @allowJs: true +// @allowSyntheticDefaultImports: true + +// @Filename: /a.js +////Object.defineProperty(exports, "__esModule", { +//// value: true +////}); +////exports.default = f; +/////** +//// * Run this function +//// * @param {string} t +//// */ +////function f(t) {} + +// @Filename: /b.js +////import f from "./a" +/////**/f + +verify.quickInfoAt("", `(alias) (property) f: (t: string) => void +import f`, "Run this function"); // Passes \ No newline at end of file From 66d2bb707bd1f5cfaa05517cce9f6fb1be10b3cd Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 24 Sep 2018 14:12:27 -0700 Subject: [PATCH 2/4] Add support for prototype assignments, fix nits --- src/compiler/binder.ts | 7 + src/compiler/checker.ts | 22 +- src/compiler/types.ts | 3 +- src/compiler/utilities.ts | 44 ++-- src/services/navigationBar.ts | 1 + src/services/utilities.ts | 1 + ...tsObjectAssignPrototypeProperty.errors.txt | 66 ++++++ ...portsObjectAssignPrototypeProperty.symbols | 172 ++++++++++++++ ...ExportsObjectAssignPrototypeProperty.types | 220 ++++++++++++++++++ ...eckExportsObjectAssignPrototypeProperty.ts | 52 +++++ 10 files changed, 559 insertions(+), 29 deletions(-) create mode 100644 tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.errors.txt create mode 100644 tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.symbols create mode 100644 tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.types create mode 100644 tests/cases/conformance/jsdoc/checkExportsObjectAssignPrototypeProperty.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 4a40d601a3f0f..e6404abca8bef 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2194,6 +2194,8 @@ namespace ts { return bindObjectDefinePropertyAssignment(node as BindableObjectDefinePropertyCall); case AssignmentDeclarationKind.ObjectDefinePropertyExports: return bindObjectDefinePropertyExport(node as BindableObjectDefinePropertyCall); + case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: + return bindObjectDefinePrototypeProperty(node as BindableObjectDefinePropertyCall); case AssignmentDeclarationKind.None: break; // Nothing to do default: @@ -2495,6 +2497,11 @@ namespace ts { bindPropertyAssignment(lhs.expression, lhs, /*isPrototypeProperty*/ false); } + function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) { + const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression); + bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true); + } + /** * For `x.prototype.y = z`, declare a member `y` on `x` if `x` is a function or class, or not declared. * Note that jsdoc preceding an ExpressionStatement like `x.prototype.y;` is also treated as a declaration. diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e5cc6751f6da7..6d68d10f1b636 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16045,6 +16045,25 @@ namespace ts { getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.Prototype) { return (container.parent.parent.parent.left as PropertyAccessExpression).expression; } + else if (container.kind === SyntaxKind.FunctionExpression && + isPropertyAssignment(container.parent) && + isIdentifier(container.parent.name) && + (container.parent.name.escapedText === "value" || container.parent.name.escapedText === "get" || container.parent.name.escapedText === "set") && + isObjectLiteralExpression(container.parent.parent) && + isCallExpression(container.parent.parent.parent) && + container.parent.parent.parent.arguments[2] === container.parent.parent && + getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) { + return (container.parent.parent.parent.arguments[0] as PropertyAccessExpression).expression; + } + else if (isMethodDeclaration(container) && + isIdentifier(container.name) && + (container.name.escapedText === "value" || container.name.escapedText === "get" || container.name.escapedText === "set") && + isObjectLiteralExpression(container.parent) && + isCallExpression(container.parent.parent) && + container.parent.parent.arguments[2] === container.parent && + getAssignmentDeclarationKind(container.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) { + return (container.parent.parent.arguments[0] as PropertyAccessExpression).expression; + } } function getTypeForThisExpressionFromJSDoc(node: Node) { @@ -16603,6 +16622,7 @@ namespace ts { return thisType && getTypeOfPropertyOfContextualType(thisType, thisAccess.name.escapedText) || false; case AssignmentDeclarationKind.ObjectDefinePropertyValue: case AssignmentDeclarationKind.ObjectDefinePropertyExports: + case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: return Debug.fail("Unimplemented"); default: return Debug.assertNever(kind); @@ -21335,7 +21355,7 @@ namespace ts { // Variables declared with 'const' // Get accessors without matching set accessors // Enum members - // JS Assignments with writable false or no setter + // Object.defineProperty assignments with writable false or no setter // Unions and intersections of the above (unions and intersections eagerly set isReadonly on creation) return !!(getCheckFlags(symbol) & CheckFlags.Readonly || symbol.flags & SymbolFlags.Property && getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.Readonly || diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d7788de9205e5..4af45a78d5322 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1700,7 +1700,6 @@ namespace ts { /** @internal */ export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: EntityNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } }; - // see: https://tc39.github.io/ecma262/#prod-SuperCall export interface SuperCall extends CallExpression { expression: SuperExpression; @@ -4269,6 +4268,8 @@ namespace ts { ObjectDefinePropertyValue, // Object.defineProperty(exports || module.exports, 'name', ...); ObjectDefinePropertyExports, + // Object.defineProperty(Foo.prototype, 'name', ...); + ObjectDefinePrototypeProperty, } /** @deprecated Use FileExtensionInfo instead. */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 48e4c80a453f9..c81faf20aa544 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1789,25 +1789,6 @@ namespace ts { return init && getExpandoInitializer(init, isPrototypeAccess(node.name)); } - export function getDescriptorInitializerOfKind(props: NodeArray, kind: "value" | "get" | "set") { - const init = find(props, p => !!p.name && getTextOfPropertyName(p.name) === kind); - if (init) { - switch (init.kind) { - case SyntaxKind.PropertyAssignment: - return init.initializer; - case SyntaxKind.ShorthandPropertyAssignment: - case SyntaxKind.MethodDeclaration: - case SyntaxKind.GetAccessor: - return init; - case SyntaxKind.SetAccessor: - return undefined; - default: - return Debug.fail("Spread assignments could never have the name 'value'"); - } - } - return undefined; - } - /** * Get the assignment 'initializer' -- the righthand side-- when the initializer is container-like (See getExpandoInitializer). * We treat the right hand side of assignments with container-like initalizers as declarations. @@ -1930,13 +1911,13 @@ namespace ts { } export function isBindableObjectDefinePropertyCall(expr: CallExpression): expr is BindableObjectDefinePropertyCall { - return !(length(expr.arguments) !== 3 || - !isPropertyAccessExpression(expr.expression) || - !isIdentifier(expr.expression.expression) || - idText(expr.expression.expression) !== "Object" || - idText(expr.expression.name) !== "defineProperty" || - !isStringOrNumericLiteralLike(expr.arguments[1]) || - !isEntityNameExpression(expr.arguments[0])); + return length(expr.arguments) === 3 && + isPropertyAccessExpression(expr.expression) && + isIdentifier(expr.expression.expression) && + idText(expr.expression.expression) === "Object" && + idText(expr.expression.name) === "defineProperty" && + isStringOrNumericLiteralLike(expr.arguments[1]) && + isEntityNameExpression(expr.arguments[0]); } function getAssignmentDeclarationKindWorker(expr: BinaryExpression | CallExpression): AssignmentDeclarationKind { @@ -1945,9 +1926,17 @@ namespace ts { return AssignmentDeclarationKind.None; } const entityName = expr.arguments[0]; - if ((isIdentifier(entityName) && entityName.escapedText === "exports") || (isPropertyAccessExpression(entityName) && isIdentifier(entityName.expression) && entityName.expression.escapedText === "module" && entityName.name.escapedText === "exports")) { + if ((isIdentifier(entityName) && entityName.escapedText === "exports") || + (isPropertyAccessExpression(entityName) && + isIdentifier(entityName.expression) && + entityName.expression.escapedText === "module" && + entityName.name.escapedText === "exports") + ) { return AssignmentDeclarationKind.ObjectDefinePropertyExports; } + if (isPropertyAccessExpression(entityName) && entityName.name.escapedText === "prototype" && isEntityNameExpression(entityName.expression)) { + return AssignmentDeclarationKind.ObjectDefinePrototypeProperty; + } return AssignmentDeclarationKind.ObjectDefinePropertyValue; } if (expr.operatorToken.kind !== SyntaxKind.EqualsToken || @@ -5027,6 +5016,7 @@ namespace ts { return ((expr as BinaryExpression).left as PropertyAccessExpression).name; case AssignmentDeclarationKind.ObjectDefinePropertyValue: case AssignmentDeclarationKind.ObjectDefinePropertyExports: + case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: return (expr as BindableObjectDefinePropertyCall).arguments[1]; default: return undefined; diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index 47bed08f21ca4..dab735f5fb524 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -283,6 +283,7 @@ namespace ts.NavigationBar { case AssignmentDeclarationKind.None: case AssignmentDeclarationKind.ObjectDefinePropertyValue: case AssignmentDeclarationKind.ObjectDefinePropertyExports: + case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: break; default: Debug.assertNever(special); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 744ebf6c8d6dd..00f62aa3c418b 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -370,6 +370,7 @@ namespace ts { switch (kind) { case AssignmentDeclarationKind.ObjectDefinePropertyValue: case AssignmentDeclarationKind.ObjectDefinePropertyExports: + case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: case AssignmentDeclarationKind.None: return ScriptElementKind.unknown; case AssignmentDeclarationKind.ExportsProperty: diff --git a/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.errors.txt b/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.errors.txt new file mode 100644 index 0000000000000..c2621a71b9915 --- /dev/null +++ b/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.errors.txt @@ -0,0 +1,66 @@ +tests/cases/conformance/jsdoc/validator.ts(19,4): error TS2540: Cannot assign to 'readonlyProp' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validator.ts(20,4): error TS2540: Cannot assign to 'readonlyAccessor' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validator.ts(21,1): error TS2322: Type '"no"' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/validator.ts(22,1): error TS2322: Type '"no"' is not assignable to type 'number'. +tests/cases/conformance/jsdoc/validator.ts(23,1): error TS2322: Type '0' is not assignable to type 'string'. + + +==== tests/cases/conformance/jsdoc/validator.ts (5 errors) ==== + import "./"; + + import Person = require("./mod1"); + + const m1 = new Person("Name") + + m1.thing; + m1.readonlyProp; + m1.rwAccessors; + m1.readonlyAccessor; + m1.setonlyAccessor; + + // allowed assignments + m1.thing = 10; + m1.rwAccessors = 11; + m1.setonlyAccessor = "yes"; + + // disallowed assignments + m1.readonlyProp = "name"; + ~~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'readonlyProp' because it is a constant or a read-only property. + m1.readonlyAccessor = 12; + ~~~~~~~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'readonlyAccessor' because it is a constant or a read-only property. + m1.thing = "no"; + ~~~~~~~~ +!!! error TS2322: Type '"no"' is not assignable to type 'number'. + m1.rwAccessors = "no"; + ~~~~~~~~~~~~~~ +!!! error TS2322: Type '"no"' is not assignable to type 'number'. + m1.setonlyAccessor = 0; + ~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type '0' is not assignable to type 'string'. + + +==== tests/cases/conformance/jsdoc/mod1.js (0 errors) ==== + /** + * @constructor + * @param {string} name + */ + function Person(name) { + this.name = name; + } + Person.prototype.describe = function () { + return "Person called " + this.name; + }; + Object.defineProperty(Person.prototype, "thing", { value: 42, writable: true }); + Object.defineProperty(Person.prototype, "readonlyProp", { value: "Smith", writable: false }); + Object.defineProperty(Person.prototype, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); + Object.defineProperty(Person.prototype, "readonlyAccessor", { get() { return 21.75 } }); + Object.defineProperty(Person.prototype, "setonlyAccessor", { + /** @param {string} str */ + set(str) { + this.rwAccessors = Number(str) + } + }); + module.exports = Person; + \ No newline at end of file diff --git a/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.symbols b/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.symbols new file mode 100644 index 0000000000000..9f04a3600cce9 --- /dev/null +++ b/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.symbols @@ -0,0 +1,172 @@ +=== tests/cases/conformance/jsdoc/validator.ts === +import "./"; + +import Person = require("./mod1"); +>Person : Symbol(Person, Decl(validator.ts, 0, 12)) + +const m1 = new Person("Name") +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>Person : Symbol(Person, Decl(validator.ts, 0, 12)) + +m1.thing; +>m1.thing : Symbol(Person["thing"], Decl(mod1.js, 9, 2)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>thing : Symbol(Person["thing"], Decl(mod1.js, 9, 2)) + +m1.readonlyProp; +>m1.readonlyProp : Symbol(Person["readonlyProp"], Decl(mod1.js, 10, 80)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>readonlyProp : Symbol(Person["readonlyProp"], Decl(mod1.js, 10, 80)) + +m1.rwAccessors; +>m1.rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) + +m1.readonlyAccessor; +>m1.readonlyAccessor : Symbol(Person["readonlyAccessor"], Decl(mod1.js, 12, 106)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>readonlyAccessor : Symbol(Person["readonlyAccessor"], Decl(mod1.js, 12, 106)) + +m1.setonlyAccessor; +>m1.setonlyAccessor : Symbol(Person["setonlyAccessor"], Decl(mod1.js, 13, 88)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>setonlyAccessor : Symbol(Person["setonlyAccessor"], Decl(mod1.js, 13, 88)) + +// allowed assignments +m1.thing = 10; +>m1.thing : Symbol(Person["thing"], Decl(mod1.js, 9, 2)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>thing : Symbol(Person["thing"], Decl(mod1.js, 9, 2)) + +m1.rwAccessors = 11; +>m1.rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) + +m1.setonlyAccessor = "yes"; +>m1.setonlyAccessor : Symbol(Person["setonlyAccessor"], Decl(mod1.js, 13, 88)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>setonlyAccessor : Symbol(Person["setonlyAccessor"], Decl(mod1.js, 13, 88)) + +// disallowed assignments +m1.readonlyProp = "name"; +>m1.readonlyProp : Symbol(Person["readonlyProp"], Decl(mod1.js, 10, 80)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>readonlyProp : Symbol(Person["readonlyProp"], Decl(mod1.js, 10, 80)) + +m1.readonlyAccessor = 12; +>m1.readonlyAccessor : Symbol(Person["readonlyAccessor"], Decl(mod1.js, 12, 106)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>readonlyAccessor : Symbol(Person["readonlyAccessor"], Decl(mod1.js, 12, 106)) + +m1.thing = "no"; +>m1.thing : Symbol(Person["thing"], Decl(mod1.js, 9, 2)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>thing : Symbol(Person["thing"], Decl(mod1.js, 9, 2)) + +m1.rwAccessors = "no"; +>m1.rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) + +m1.setonlyAccessor = 0; +>m1.setonlyAccessor : Symbol(Person["setonlyAccessor"], Decl(mod1.js, 13, 88)) +>m1 : Symbol(m1, Decl(validator.ts, 4, 5)) +>setonlyAccessor : Symbol(Person["setonlyAccessor"], Decl(mod1.js, 13, 88)) + + +=== tests/cases/conformance/jsdoc/mod1.js === +/** + * @constructor + * @param {string} name + */ +function Person(name) { +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>name : Symbol(name, Decl(mod1.js, 4, 16)) + + this.name = name; +>this.name : Symbol(Person.name, Decl(mod1.js, 4, 23)) +>this : Symbol(Person, Decl(mod1.js, 0, 0)) +>name : Symbol(Person.name, Decl(mod1.js, 4, 23)) +>name : Symbol(name, Decl(mod1.js, 4, 16)) +} +Person.prototype.describe = function () { +>Person.prototype : Symbol(Person.describe, Decl(mod1.js, 6, 1)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>describe : Symbol(Person.describe, Decl(mod1.js, 6, 1)) + + return "Person called " + this.name; +>this.name : Symbol(Person.name, Decl(mod1.js, 4, 23)) +>this : Symbol(Person, Decl(mod1.js, 0, 0)) +>name : Symbol(Person.name, Decl(mod1.js, 4, 23)) + +}; +Object.defineProperty(Person.prototype, "thing", { value: 42, writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Person.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(mod1.js, 10, 50)) +>writable : Symbol(writable, Decl(mod1.js, 10, 61)) + +Object.defineProperty(Person.prototype, "readonlyProp", { value: "Smith", writable: false }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Person.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(mod1.js, 11, 57)) +>writable : Symbol(writable, Decl(mod1.js, 11, 73)) + +Object.defineProperty(Person.prototype, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Person.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>get : Symbol(get, Decl(mod1.js, 12, 56)) +>set : Symbol(set, Decl(mod1.js, 12, 80)) +>_ : Symbol(_, Decl(mod1.js, 12, 85)) + +Object.defineProperty(Person.prototype, "readonlyAccessor", { get() { return 21.75 } }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Person.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>get : Symbol(get, Decl(mod1.js, 13, 61)) + +Object.defineProperty(Person.prototype, "setonlyAccessor", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Person.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) + + /** @param {string} str */ + set(str) { +>set : Symbol(set, Decl(mod1.js, 14, 60)) +>str : Symbol(str, Decl(mod1.js, 16, 8)) + + this.rwAccessors = Number(str) +>this.rwAccessors : Symbol(Person["rwAccessors"], Decl(mod1.js, 11, 93)) +>this : Symbol(Person, Decl(mod1.js, 0, 0)) +>rwAccessors : Symbol(rwAccessors, Decl(mod1.js, 16, 14)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>str : Symbol(str, Decl(mod1.js, 16, 8)) + } +}); +module.exports = Person; +>module.exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>module : Symbol(export=, Decl(mod1.js, 19, 3)) +>exports : Symbol(export=, Decl(mod1.js, 19, 3)) +>Person : Symbol(Person, Decl(mod1.js, 0, 0)) + diff --git a/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.types b/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.types new file mode 100644 index 0000000000000..662c578701f56 --- /dev/null +++ b/tests/baselines/reference/checkExportsObjectAssignPrototypeProperty.types @@ -0,0 +1,220 @@ +=== tests/cases/conformance/jsdoc/validator.ts === +import "./"; + +import Person = require("./mod1"); +>Person : typeof Person + +const m1 = new Person("Name") +>m1 : Person +>new Person("Name") : Person +>Person : typeof Person +>"Name" : "Name" + +m1.thing; +>m1.thing : number +>m1 : Person +>thing : number + +m1.readonlyProp; +>m1.readonlyProp : string +>m1 : Person +>readonlyProp : string + +m1.rwAccessors; +>m1.rwAccessors : number +>m1 : Person +>rwAccessors : number + +m1.readonlyAccessor; +>m1.readonlyAccessor : number +>m1 : Person +>readonlyAccessor : number + +m1.setonlyAccessor; +>m1.setonlyAccessor : string +>m1 : Person +>setonlyAccessor : string + +// allowed assignments +m1.thing = 10; +>m1.thing = 10 : 10 +>m1.thing : number +>m1 : Person +>thing : number +>10 : 10 + +m1.rwAccessors = 11; +>m1.rwAccessors = 11 : 11 +>m1.rwAccessors : number +>m1 : Person +>rwAccessors : number +>11 : 11 + +m1.setonlyAccessor = "yes"; +>m1.setonlyAccessor = "yes" : "yes" +>m1.setonlyAccessor : string +>m1 : Person +>setonlyAccessor : string +>"yes" : "yes" + +// disallowed assignments +m1.readonlyProp = "name"; +>m1.readonlyProp = "name" : "name" +>m1.readonlyProp : any +>m1 : Person +>readonlyProp : any +>"name" : "name" + +m1.readonlyAccessor = 12; +>m1.readonlyAccessor = 12 : 12 +>m1.readonlyAccessor : any +>m1 : Person +>readonlyAccessor : any +>12 : 12 + +m1.thing = "no"; +>m1.thing = "no" : "no" +>m1.thing : number +>m1 : Person +>thing : number +>"no" : "no" + +m1.rwAccessors = "no"; +>m1.rwAccessors = "no" : "no" +>m1.rwAccessors : number +>m1 : Person +>rwAccessors : number +>"no" : "no" + +m1.setonlyAccessor = 0; +>m1.setonlyAccessor = 0 : 0 +>m1.setonlyAccessor : string +>m1 : Person +>setonlyAccessor : string +>0 : 0 + + +=== tests/cases/conformance/jsdoc/mod1.js === +/** + * @constructor + * @param {string} name + */ +function Person(name) { +>Person : typeof Person +>name : string + + this.name = name; +>this.name = name : string +>this.name : string +>this : Person +>name : string +>name : string +} +Person.prototype.describe = function () { +>Person.prototype.describe = function () { return "Person called " + this.name;} : () => string +>Person.prototype.describe : any +>Person.prototype : any +>Person : typeof Person +>prototype : any +>describe : any +>function () { return "Person called " + this.name;} : () => string + + return "Person called " + this.name; +>"Person called " + this.name : string +>"Person called " : "Person called " +>this.name : string +>this : Person +>name : string + +}; +Object.defineProperty(Person.prototype, "thing", { value: 42, writable: true }); +>Object.defineProperty(Person.prototype, "thing", { value: 42, writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Person.prototype : any +>Person : typeof Person +>prototype : any +>"thing" : "thing" +>{ value: 42, writable: true } : { value: number; writable: true; } +>value : number +>42 : 42 +>writable : true +>true : true + +Object.defineProperty(Person.prototype, "readonlyProp", { value: "Smith", writable: false }); +>Object.defineProperty(Person.prototype, "readonlyProp", { value: "Smith", writable: false }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Person.prototype : any +>Person : typeof Person +>prototype : any +>"readonlyProp" : "readonlyProp" +>{ value: "Smith", writable: false } : { value: string; writable: false; } +>value : string +>"Smith" : "Smith" +>writable : false +>false : false + +Object.defineProperty(Person.prototype, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +>Object.defineProperty(Person.prototype, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Person.prototype : any +>Person : typeof Person +>prototype : any +>"rwAccessors" : "rwAccessors" +>{ get() { return 98122 }, set(_) { /*ignore*/ } } : { get(): number; set(_: any): void; } +>get : () => number +>98122 : 98122 +>set : (_: any) => void +>_ : any + +Object.defineProperty(Person.prototype, "readonlyAccessor", { get() { return 21.75 } }); +>Object.defineProperty(Person.prototype, "readonlyAccessor", { get() { return 21.75 } }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Person.prototype : any +>Person : typeof Person +>prototype : any +>"readonlyAccessor" : "readonlyAccessor" +>{ get() { return 21.75 } } : { get(): number; } +>get : () => number +>21.75 : 21.75 + +Object.defineProperty(Person.prototype, "setonlyAccessor", { +>Object.defineProperty(Person.prototype, "setonlyAccessor", { /** @param {string} str */ set(str) { this.rwAccessors = Number(str) }}) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Person.prototype : any +>Person : typeof Person +>prototype : any +>"setonlyAccessor" : "setonlyAccessor" +>{ /** @param {string} str */ set(str) { this.rwAccessors = Number(str) }} : { set(str: string): void; } + + /** @param {string} str */ + set(str) { +>set : (str: string) => void +>str : string + + this.rwAccessors = Number(str) +>this.rwAccessors = Number(str) : number +>this.rwAccessors : number +>this : Person +>rwAccessors : number +>Number(str) : number +>Number : NumberConstructor +>str : string + } +}); +module.exports = Person; +>module.exports = Person : typeof Person +>module.exports : typeof Person +>module : { "tests/cases/conformance/jsdoc/mod1": typeof Person; } +>exports : typeof Person +>Person : typeof Person + diff --git a/tests/cases/conformance/jsdoc/checkExportsObjectAssignPrototypeProperty.ts b/tests/cases/conformance/jsdoc/checkExportsObjectAssignPrototypeProperty.ts new file mode 100644 index 0000000000000..9518e05264654 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkExportsObjectAssignPrototypeProperty.ts @@ -0,0 +1,52 @@ +// @allowJs: true +// @noEmit: true +// @strict: true +// @checkJs: true +// @filename: mod1.js +/** + * @constructor + * @param {string} name + */ +function Person(name) { + this.name = name; +} +Person.prototype.describe = function () { + return "Person called " + this.name; +}; +Object.defineProperty(Person.prototype, "thing", { value: 42, writable: true }); +Object.defineProperty(Person.prototype, "readonlyProp", { value: "Smith", writable: false }); +Object.defineProperty(Person.prototype, "rwAccessors", { get() { return 98122 }, set(_) { /*ignore*/ } }); +Object.defineProperty(Person.prototype, "readonlyAccessor", { get() { return 21.75 } }); +Object.defineProperty(Person.prototype, "setonlyAccessor", { + /** @param {string} str */ + set(str) { + this.rwAccessors = Number(str) + } +}); +module.exports = Person; + +// @filename: validator.ts +import "./"; + +import Person = require("./mod1"); + +const m1 = new Person("Name") + +m1.thing; +m1.readonlyProp; +m1.rwAccessors; +m1.readonlyAccessor; +m1.setonlyAccessor; + +// allowed assignments +m1.thing = 10; +m1.rwAccessors = 11; +m1.setonlyAccessor = "yes"; + +// disallowed assignments +m1.readonlyProp = "name"; +m1.readonlyAccessor = 12; +m1.thing = "no"; +m1.rwAccessors = "no"; +m1.setonlyAccessor = 0; + From 01589fb3b9642466439007efca67fa12d365a3e6 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 10 Oct 2018 13:26:53 -0700 Subject: [PATCH 3/4] Fix code review comments --- src/compiler/checker.ts | 18 ++- src/compiler/utilities.ts | 9 +- .../checkObjectDefineProperty.errors.txt | 18 ++- .../checkObjectDefineProperty.symbols | 127 ++++++++++-------- .../reference/checkObjectDefineProperty.types | 37 ++++- ...ntDefineProperrtyPotentialMerge.errors.txt | 23 ++++ ...nmentDefineProperrtyPotentialMerge.symbols | 59 ++++++++ ...ignmentDefineProperrtyPotentialMerge.types | 79 +++++++++++ ...AssignmentDefineProperrtyPotentialMerge.ts | 17 +++ .../jsdoc/checkObjectDefineProperty.ts | 7 +- 10 files changed, 311 insertions(+), 83 deletions(-) create mode 100644 tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.errors.txt create mode 100644 tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.symbols create mode 100644 tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.types create mode 100644 tests/cases/compiler/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6d68d10f1b636..c68df69d79eef 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16045,6 +16045,9 @@ namespace ts { getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.Prototype) { return (container.parent.parent.parent.left as PropertyAccessExpression).expression; } + // Object.defineProperty(x, "method", { value: function() { } }); + // Object.defineProperty(x, "method", { set: (x: () => void) => void }); + // Object.defineProperty(x, "method", { get: () => function() { }) }); else if (container.kind === SyntaxKind.FunctionExpression && isPropertyAssignment(container.parent) && isIdentifier(container.parent.name) && @@ -16055,6 +16058,9 @@ namespace ts { getAssignmentDeclarationKind(container.parent.parent.parent) === AssignmentDeclarationKind.ObjectDefinePrototypeProperty) { return (container.parent.parent.parent.arguments[0] as PropertyAccessExpression).expression; } + // Object.defineProperty(x, "method", { value() { } }); + // Object.defineProperty(x, "method", { set(x: () => void) {} }); + // Object.defineProperty(x, "method", { get() { return () => {} } }); else if (isMethodDeclaration(container) && isIdentifier(container.name) && (container.name.escapedText === "value" || container.name.escapedText === "get" || container.name.escapedText === "set") && @@ -16623,7 +16629,7 @@ namespace ts { case AssignmentDeclarationKind.ObjectDefinePropertyValue: case AssignmentDeclarationKind.ObjectDefinePropertyExports: case AssignmentDeclarationKind.ObjectDefinePrototypeProperty: - return Debug.fail("Unimplemented"); + return Debug.fail("Does not apply"); default: return Debug.assertNever(kind); } @@ -21328,16 +21334,16 @@ namespace ts { const objectLitType = checkExpressionCached(d.arguments[2]); const valueType = getTypeOfPropertyOfType(objectLitType, "value" as __String); if (valueType) { - const writableType = getTypeOfPropertyOfType(objectLitType, "writable" as __String); + const writableProp = getPropertyOfType(objectLitType, "writable" as __String); + const writableType = writableProp && getTypeOfSymbol(writableProp); if (!writableType || writableType === falseType || writableType === regularFalseType) { return true; } // We include this definition whereupon we walk back and check the type at the declaration because - // The usual definition of `Object.defineProperty` will _not_ cause literal type to be preserved in the + // The usual definition of `Object.defineProperty` will _not_ cause literal types to be preserved in the // argument types, should the type be contextualized by the call itself. - const writableProp = getPropertyOfType(objectLitType, "writable" as __String); - if (writableProp!.valueDeclaration && isPropertyAssignment(writableProp!.valueDeclaration)) { - const initializer = (writableProp!.valueDeclaration as PropertyAssignment).initializer; + if (writableProp && writableProp.valueDeclaration && isPropertyAssignment(writableProp.valueDeclaration)) { + const initializer = writableProp.valueDeclaration.initializer; const rawOriginalType = checkExpression(initializer); if (rawOriginalType === falseType || rawOriginalType === regularFalseType) { return true; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index c81faf20aa544..7c03682565c68 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1926,12 +1926,7 @@ namespace ts { return AssignmentDeclarationKind.None; } const entityName = expr.arguments[0]; - if ((isIdentifier(entityName) && entityName.escapedText === "exports") || - (isPropertyAccessExpression(entityName) && - isIdentifier(entityName.expression) && - entityName.expression.escapedText === "module" && - entityName.name.escapedText === "exports") - ) { + if (isExportsIdentifier(entityName) || isModuleExportsPropertyAccessExpression(entityName)) { return AssignmentDeclarationKind.ObjectDefinePropertyExports; } if (isPropertyAccessExpression(entityName) && entityName.name.escapedText === "prototype" && isEntityNameExpression(entityName.expression)) { @@ -1955,7 +1950,7 @@ namespace ts { if (lhs.expression.kind === SyntaxKind.ThisKeyword) { return AssignmentDeclarationKind.ThisProperty; } - else if (isIdentifier(lhs.expression) && lhs.expression.escapedText === "module" && lhs.name.escapedText === "exports") { + else if (isModuleExportsPropertyAccessExpression(lhs)) { // module.exports = expr return AssignmentDeclarationKind.ModuleExports; } diff --git a/tests/baselines/reference/checkObjectDefineProperty.errors.txt b/tests/baselines/reference/checkObjectDefineProperty.errors.txt index 44ffcd723e9bb..b587955ef2f81 100644 --- a/tests/baselines/reference/checkObjectDefineProperty.errors.txt +++ b/tests/baselines/reference/checkObjectDefineProperty.errors.txt @@ -1,18 +1,20 @@ -tests/cases/conformance/jsdoc/validate.ts(13,3): error TS2540: Cannot assign to 'lastName' because it is a constant or a read-only property. -tests/cases/conformance/jsdoc/validate.ts(14,3): error TS2540: Cannot assign to 'houseNumber' because it is a constant or a read-only property. -tests/cases/conformance/jsdoc/validate.ts(15,1): error TS2322: Type '12' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/validate.ts(14,3): error TS2540: Cannot assign to 'lastName' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validate.ts(15,3): error TS2540: Cannot assign to 'houseNumber' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/validate.ts(16,1): error TS2322: Type '12' is not assignable to type 'string'. +tests/cases/conformance/jsdoc/validate.ts(17,3): error TS2540: Cannot assign to 'middleInit' because it is a constant or a read-only property. -==== tests/cases/conformance/jsdoc/validate.ts (3 errors) ==== +==== tests/cases/conformance/jsdoc/validate.ts (4 errors) ==== // Validate in TS as simple validations would usually be interpreted as more special assignments import x = require("./"); x.name; + x.middleInit; x.lastName; x.zip; x.houseNumber; x.zipStr; - x.name = "Another"; + x.name = "Another"; x.zip = 98123; x.zipStr = "OK"; @@ -25,10 +27,14 @@ tests/cases/conformance/jsdoc/validate.ts(15,1): error TS2322: Type '12' is not x.zipStr = 12; // should fail ~~~~~~~~ !!! error TS2322: Type '12' is not assignable to type 'string'. + x.middleInit = "R"; // should also fail + ~~~~~~~~~~ +!!! error TS2540: Cannot assign to 'middleInit' because it is a constant or a read-only property. ==== tests/cases/conformance/jsdoc/index.js (0 errors) ==== const x = {}; Object.defineProperty(x, "name", { value: "Charles", writable: true }); + Object.defineProperty(x, "middleInit", { value: "H" }); Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); @@ -58,7 +64,7 @@ tests/cases/conformance/jsdoc/validate.ts(15,1): error TS2322: Type '12' is not const returnExemplar = () => x; const needsExemplar = (_ = x) => void 0; - const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); + const expected = /** @type {{name: string, readonly middleInit: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); /** * diff --git a/tests/baselines/reference/checkObjectDefineProperty.symbols b/tests/baselines/reference/checkObjectDefineProperty.symbols index 96973d3653402..d3eea1a0a7461 100644 --- a/tests/baselines/reference/checkObjectDefineProperty.symbols +++ b/tests/baselines/reference/checkObjectDefineProperty.symbols @@ -8,55 +8,65 @@ x.name; >x : Symbol(x, Decl(validate.ts, 0, 0)) >name : Symbol(x["name"], Decl(index.js, 0, 13)) +x.middleInit; +>x.middleInit : Symbol(x["middleInit"], Decl(index.js, 1, 71)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>middleInit : Symbol(x["middleInit"], Decl(index.js, 1, 71)) + x.lastName; ->x.lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) +>x.lastName : Symbol(x["lastName"], Decl(index.js, 2, 55)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) +>lastName : Symbol(x["lastName"], Decl(index.js, 2, 55)) x.zip; ->x.zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>x.zip : Symbol(x["zip"], Decl(index.js, 3, 74)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>zip : Symbol(x["zip"], Decl(index.js, 3, 74)) x.houseNumber; ->x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 4, 83)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>houseNumber : Symbol(x["houseNumber"], Decl(index.js, 4, 83)) x.zipStr; ->x.zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>x.zipStr : Symbol(x["zipStr"], Decl(index.js, 5, 68)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>zipStr : Symbol(x["zipStr"], Decl(index.js, 5, 68)) -x.name = "Another"; +x.name = "Another"; >x.name : Symbol(x["name"], Decl(index.js, 0, 13)) >x : Symbol(x, Decl(validate.ts, 0, 0)) >name : Symbol(x["name"], Decl(index.js, 0, 13)) x.zip = 98123; ->x.zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>x.zip : Symbol(x["zip"], Decl(index.js, 3, 74)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>zip : Symbol(x["zip"], Decl(index.js, 3, 74)) x.zipStr = "OK"; ->x.zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>x.zipStr : Symbol(x["zipStr"], Decl(index.js, 5, 68)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>zipStr : Symbol(x["zipStr"], Decl(index.js, 5, 68)) x.lastName = "should fail"; ->x.lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) +>x.lastName : Symbol(x["lastName"], Decl(index.js, 2, 55)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->lastName : Symbol(x["lastName"], Decl(index.js, 1, 71)) +>lastName : Symbol(x["lastName"], Decl(index.js, 2, 55)) x.houseNumber = 12; // should also fail ->x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 4, 83)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>houseNumber : Symbol(x["houseNumber"], Decl(index.js, 4, 83)) x.zipStr = 12; // should fail ->x.zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>x.zipStr : Symbol(x["zipStr"], Decl(index.js, 5, 68)) >x : Symbol(x, Decl(validate.ts, 0, 0)) ->zipStr : Symbol(x["zipStr"], Decl(index.js, 4, 68)) +>zipStr : Symbol(x["zipStr"], Decl(index.js, 5, 68)) + +x.middleInit = "R"; // should also fail +>x.middleInit : Symbol(x["middleInit"], Decl(index.js, 1, 71)) +>x : Symbol(x, Decl(validate.ts, 0, 0)) +>middleInit : Symbol(x["middleInit"], Decl(index.js, 1, 71)) === tests/cases/conformance/jsdoc/index.js === const x = {}; @@ -70,29 +80,36 @@ Object.defineProperty(x, "name", { value: "Charles", writable: true }); >value : Symbol(value, Decl(index.js, 1, 34)) >writable : Symbol(writable, Decl(index.js, 1, 52)) +Object.defineProperty(x, "middleInit", { value: "H" }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) +>value : Symbol(value, Decl(index.js, 2, 40)) + Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); >Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) ->value : Symbol(value, Decl(index.js, 2, 38)) ->writable : Symbol(writable, Decl(index.js, 2, 54)) +>value : Symbol(value, Decl(index.js, 3, 38)) +>writable : Symbol(writable, Decl(index.js, 3, 54)) Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); >Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) ->get : Symbol(get, Decl(index.js, 3, 33)) ->set : Symbol(set, Decl(index.js, 3, 57)) ->_ : Symbol(_, Decl(index.js, 3, 62)) +>get : Symbol(get, Decl(index.js, 4, 33)) +>set : Symbol(set, Decl(index.js, 4, 57)) +>_ : Symbol(_, Decl(index.js, 4, 62)) Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); >Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) >Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) >defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) ->get : Symbol(get, Decl(index.js, 4, 41)) +>get : Symbol(get, Decl(index.js, 5, 41)) Object.defineProperty(x, "zipStr", { >Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) @@ -102,13 +119,13 @@ Object.defineProperty(x, "zipStr", { /** @param {string} str */ set(str) { ->set : Symbol(set, Decl(index.js, 5, 36)) ->str : Symbol(str, Decl(index.js, 7, 8)) +>set : Symbol(set, Decl(index.js, 6, 36)) +>str : Symbol(str, Decl(index.js, 8, 8)) this.zip = Number(str) ->zip : Symbol(zip, Decl(index.js, 7, 14)) +>zip : Symbol(zip, Decl(index.js, 8, 14)) >Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->str : Symbol(str, Decl(index.js, 7, 8)) +>str : Symbol(str, Decl(index.js, 8, 8)) } }); @@ -116,45 +133,45 @@ Object.defineProperty(x, "zipStr", { * @param {{name: string}} named */ function takeName(named) { return named.name; } ->takeName : Symbol(takeName, Decl(index.js, 10, 3)) ->named : Symbol(named, Decl(index.js, 15, 18)) ->named.name : Symbol(name, Decl(index.js, 13, 12)) ->named : Symbol(named, Decl(index.js, 15, 18)) ->name : Symbol(name, Decl(index.js, 13, 12)) +>takeName : Symbol(takeName, Decl(index.js, 11, 3)) +>named : Symbol(named, Decl(index.js, 16, 18)) +>named.name : Symbol(name, Decl(index.js, 14, 12)) +>named : Symbol(named, Decl(index.js, 16, 18)) +>name : Symbol(name, Decl(index.js, 14, 12)) takeName(x); ->takeName : Symbol(takeName, Decl(index.js, 10, 3)) +>takeName : Symbol(takeName, Decl(index.js, 11, 3)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) /** * @type {number} */ var a = x.zip; ->a : Symbol(a, Decl(index.js, 21, 3)) ->x.zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>a : Symbol(a, Decl(index.js, 22, 3)) +>x.zip : Symbol(x["zip"], Decl(index.js, 3, 74)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) ->zip : Symbol(x["zip"], Decl(index.js, 2, 74)) +>zip : Symbol(x["zip"], Decl(index.js, 3, 74)) /** * @type {number} */ var b = x.houseNumber; ->b : Symbol(b, Decl(index.js, 26, 3)) ->x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>b : Symbol(b, Decl(index.js, 27, 3)) +>x.houseNumber : Symbol(x["houseNumber"], Decl(index.js, 4, 83)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) ->houseNumber : Symbol(x["houseNumber"], Decl(index.js, 3, 83)) +>houseNumber : Symbol(x["houseNumber"], Decl(index.js, 4, 83)) const returnExemplar = () => x; ->returnExemplar : Symbol(returnExemplar, Decl(index.js, 28, 5)) +>returnExemplar : Symbol(returnExemplar, Decl(index.js, 29, 5)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) const needsExemplar = (_ = x) => void 0; ->needsExemplar : Symbol(needsExemplar, Decl(index.js, 29, 5)) ->_ : Symbol(_, Decl(index.js, 29, 23)) +>needsExemplar : Symbol(needsExemplar, Decl(index.js, 30, 5)) +>_ : Symbol(_, Decl(index.js, 30, 23)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) -const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); ->expected : Symbol(expected, Decl(index.js, 31, 5)) +const expected = /** @type {{name: string, readonly middleInit: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); +>expected : Symbol(expected, Decl(index.js, 32, 5)) /** * @@ -162,19 +179,19 @@ const expected = /** @type {{name: string, readonly lastName: string, zip: numbe * @param {typeof needsExemplar} b */ function match(a, b) {} ->match : Symbol(match, Decl(index.js, 31, 157)) ->a : Symbol(a, Decl(index.js, 38, 15)) ->b : Symbol(b, Decl(index.js, 38, 17)) +>match : Symbol(match, Decl(index.js, 32, 186)) +>a : Symbol(a, Decl(index.js, 39, 15)) +>b : Symbol(b, Decl(index.js, 39, 17)) match(() => expected, (x = expected) => void 0); ->match : Symbol(match, Decl(index.js, 31, 157)) ->expected : Symbol(expected, Decl(index.js, 31, 5)) ->x : Symbol(x, Decl(index.js, 40, 23)) ->expected : Symbol(expected, Decl(index.js, 31, 5)) +>match : Symbol(match, Decl(index.js, 32, 186)) +>expected : Symbol(expected, Decl(index.js, 32, 5)) +>x : Symbol(x, Decl(index.js, 41, 23)) +>expected : Symbol(expected, Decl(index.js, 32, 5)) module.exports = x; >module.exports : Symbol("tests/cases/conformance/jsdoc/index", Decl(index.js, 0, 0)) ->module : Symbol(export=, Decl(index.js, 40, 48)) ->exports : Symbol(export=, Decl(index.js, 40, 48)) +>module : Symbol(export=, Decl(index.js, 41, 48)) +>exports : Symbol(export=, Decl(index.js, 41, 48)) >x : Symbol(x, Decl(index.js, 0, 5), Decl(index.js, 1, 22)) diff --git a/tests/baselines/reference/checkObjectDefineProperty.types b/tests/baselines/reference/checkObjectDefineProperty.types index 791024a3a5325..88a1e682dfc9e 100644 --- a/tests/baselines/reference/checkObjectDefineProperty.types +++ b/tests/baselines/reference/checkObjectDefineProperty.types @@ -8,6 +8,11 @@ x.name; >x : typeof x >name : string +x.middleInit; +>x.middleInit : string +>x : typeof x +>middleInit : string + x.lastName; >x.lastName : string >x : typeof x @@ -28,7 +33,7 @@ x.zipStr; >x : typeof x >zipStr : string -x.name = "Another"; +x.name = "Another"; >x.name = "Another" : "Another" >x.name : string >x : typeof x @@ -70,6 +75,13 @@ x.zipStr = 12; // should fail >zipStr : string >12 : 12 +x.middleInit = "R"; // should also fail +>x.middleInit = "R" : "R" +>x.middleInit : any +>x : typeof x +>middleInit : any +>"R" : "R" + === tests/cases/conformance/jsdoc/index.js === const x = {}; >x : typeof x @@ -88,6 +100,17 @@ Object.defineProperty(x, "name", { value: "Charles", writable: true }); >writable : true >true : true +Object.defineProperty(x, "middleInit", { value: "H" }); +>Object.defineProperty(x, "middleInit", { value: "H" }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>x : typeof x +>"middleInit" : "middleInit" +>{ value: "H" } : { value: string; } +>value : string +>"H" : "H" + Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); >Object.defineProperty(x, "lastName", { value: "Smith", writable: false }) : any >Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any @@ -196,9 +219,9 @@ const needsExemplar = (_ = x) => void 0; >void 0 : undefined >0 : 0 -const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); ->expected : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } ->(/** @type {*} */(null)) : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +const expected = /** @type {{name: string, readonly middleInit: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); +>expected : { name: string; readonly middleInit: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>(/** @type {*} */(null)) : { name: string; readonly middleInit: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } >(null) : any >null : null @@ -215,11 +238,11 @@ function match(a, b) {} match(() => expected, (x = expected) => void 0); >match(() => expected, (x = expected) => void 0) : void >match : (a: () => typeof x, b: (_?: typeof x) => undefined) => void ->() => expected : () => { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } ->expected : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>() => expected : () => { name: string; readonly middleInit: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>expected : { name: string; readonly middleInit: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } >(x = expected) => void 0 : (x?: typeof x | undefined) => undefined >x : typeof x | undefined ->expected : { name: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } +>expected : { name: string; readonly middleInit: string; readonly lastName: string; zip: number; readonly houseNumber: number; zipStr: string; } >void 0 : undefined >0 : 0 diff --git a/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.errors.txt b/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.errors.txt new file mode 100644 index 0000000000000..d7c036abacf3a --- /dev/null +++ b/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/namespacer.js(2,1): error TS2323: Cannot redeclare exported variable 'NS'. +tests/cases/compiler/namespacer.js(3,1): error TS2323: Cannot redeclare exported variable 'NS'. + + +==== tests/cases/compiler/index.js (0 errors) ==== + const _item = require("./namespacer"); + module.exports = 12; + Object.defineProperty(module, "exports", { value: "oh no" }); + +==== tests/cases/compiler/namespacey.js (0 errors) ==== + const A = {} + A.bar = class Q {} + module.exports = A; +==== tests/cases/compiler/namespacer.js (2 errors) ==== + const B = {} + B.NS = require("./namespacey"); + ~~~~ +!!! error TS2323: Cannot redeclare exported variable 'NS'. + Object.defineProperty(B, "NS", { value: "why though", writable: true }); + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2323: Cannot redeclare exported variable 'NS'. + module.exports = B; + \ No newline at end of file diff --git a/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.symbols b/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.symbols new file mode 100644 index 0000000000000..5b0883f227ac4 --- /dev/null +++ b/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.symbols @@ -0,0 +1,59 @@ +=== tests/cases/compiler/index.js === +const _item = require("./namespacer"); +>_item : Symbol(_item, Decl(index.js, 0, 5)) +>require : Symbol(require) +>"./namespacer" : Symbol("tests/cases/compiler/namespacer", Decl(namespacer.js, 0, 0)) + +module.exports = 12; +>module.exports : Symbol("tests/cases/compiler/index", Decl(index.js, 0, 0)) +>module : Symbol(export=, Decl(index.js, 0, 38)) +>exports : Symbol(export=, Decl(index.js, 0, 38)) + +Object.defineProperty(module, "exports", { value: "oh no" }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>module : Symbol(module, Decl(index.js, 0, 38), Decl(index.js, 2, 22)) +>value : Symbol(value, Decl(index.js, 2, 42)) + +=== tests/cases/compiler/namespacey.js === +const A = {} +>A : Symbol(A, Decl(namespacey.js, 0, 5), Decl(namespacey.js, 0, 12)) + +A.bar = class Q {} +>A.bar : Symbol(A.bar, Decl(namespacey.js, 0, 12)) +>A : Symbol(A, Decl(namespacey.js, 0, 5), Decl(namespacey.js, 0, 12)) +>bar : Symbol(A.bar, Decl(namespacey.js, 0, 12)) +>Q : Symbol(Q, Decl(namespacey.js, 1, 7)) + +module.exports = A; +>module.exports : Symbol("tests/cases/compiler/namespacey", Decl(namespacey.js, 0, 0)) +>module : Symbol(export=, Decl(namespacey.js, 1, 18)) +>exports : Symbol(export=, Decl(namespacey.js, 1, 18)) +>A : Symbol(A, Decl(namespacey.js, 0, 5), Decl(namespacey.js, 0, 12)) + +=== tests/cases/compiler/namespacer.js === +const B = {} +>B : Symbol(B, Decl(namespacer.js, 0, 5), Decl(namespacer.js, 0, 12)) + +B.NS = require("./namespacey"); +>B.NS : Symbol(B.NS, Decl(namespacer.js, 0, 12), Decl(namespacer.js, 1, 31)) +>B : Symbol(B, Decl(namespacer.js, 0, 5), Decl(namespacer.js, 0, 12)) +>NS : Symbol(B.NS, Decl(namespacer.js, 0, 12), Decl(namespacer.js, 1, 31)) +>require : Symbol(require) +>"./namespacey" : Symbol("tests/cases/compiler/namespacey", Decl(namespacey.js, 0, 0)) + +Object.defineProperty(B, "NS", { value: "why though", writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>B : Symbol(B, Decl(namespacer.js, 0, 5), Decl(namespacer.js, 0, 12)) +>value : Symbol(value, Decl(namespacer.js, 2, 32)) +>writable : Symbol(writable, Decl(namespacer.js, 2, 53)) + +module.exports = B; +>module.exports : Symbol("tests/cases/compiler/namespacer", Decl(namespacer.js, 0, 0)) +>module : Symbol(export=, Decl(namespacer.js, 2, 72)) +>exports : Symbol(export=, Decl(namespacer.js, 2, 72)) +>B : Symbol(B, Decl(namespacer.js, 0, 5), Decl(namespacer.js, 0, 12)) + diff --git a/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.types b/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.types new file mode 100644 index 0000000000000..ebd6f7ba4342d --- /dev/null +++ b/tests/baselines/reference/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.types @@ -0,0 +1,79 @@ +=== tests/cases/compiler/index.js === +const _item = require("./namespacer"); +>_item : typeof B +>require("./namespacer") : typeof B +>require : any +>"./namespacer" : "./namespacer" + +module.exports = 12; +>module.exports = 12 : number +>module.exports : number +>module : typeof module +>exports : number +>12 : 12 + +Object.defineProperty(module, "exports", { value: "oh no" }); +>Object.defineProperty(module, "exports", { value: "oh no" }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>module : typeof module +>"exports" : "exports" +>{ value: "oh no" } : { value: string; } +>value : string +>"oh no" : "oh no" + +=== tests/cases/compiler/namespacey.js === +const A = {} +>A : typeof A +>{} : {} + +A.bar = class Q {} +>A.bar = class Q {} : typeof Q +>A.bar : typeof Q +>A : typeof A +>bar : typeof Q +>class Q {} : typeof Q +>Q : typeof Q + +module.exports = A; +>module.exports = A : typeof A +>module.exports : typeof A +>module : { "tests/cases/compiler/namespacey": typeof A; } +>exports : typeof A +>A : typeof A + +=== tests/cases/compiler/namespacer.js === +const B = {} +>B : typeof B +>{} : {} + +B.NS = require("./namespacey"); +>B.NS = require("./namespacey") : typeof A +>B.NS : string | typeof A +>B : typeof B +>NS : string | typeof A +>require("./namespacey") : typeof A +>require : any +>"./namespacey" : "./namespacey" + +Object.defineProperty(B, "NS", { value: "why though", writable: true }); +>Object.defineProperty(B, "NS", { value: "why though", writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>B : typeof B +>"NS" : "NS" +>{ value: "why though", writable: true } : { value: string; writable: true; } +>value : string +>"why though" : "why though" +>writable : true +>true : true + +module.exports = B; +>module.exports = B : typeof B +>module.exports : typeof B +>module : { "tests/cases/compiler/namespacer": typeof B; } +>exports : typeof B +>B : typeof B + diff --git a/tests/cases/compiler/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.ts b/tests/cases/compiler/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.ts new file mode 100644 index 0000000000000..a0c0b325adf67 --- /dev/null +++ b/tests/cases/compiler/ensureNoCrashExportAssignmentDefineProperrtyPotentialMerge.ts @@ -0,0 +1,17 @@ +// @allowJs: true +// @noEmit: true +// @checkJs: true +// @filename: namespacey.js +const A = {} +A.bar = class Q {} +module.exports = A; +// @filename: namespacer.js +const B = {} +B.NS = require("./namespacey"); +Object.defineProperty(B, "NS", { value: "why though", writable: true }); +module.exports = B; + +// @filename: index.js +const _item = require("./namespacer"); +module.exports = 12; +Object.defineProperty(module, "exports", { value: "oh no" }); diff --git a/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts b/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts index f62a34e1c64a8..d0ba377b000c6 100644 --- a/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts +++ b/tests/cases/conformance/jsdoc/checkObjectDefineProperty.ts @@ -5,6 +5,7 @@ // @filename: index.js const x = {}; Object.defineProperty(x, "name", { value: "Charles", writable: true }); +Object.defineProperty(x, "middleInit", { value: "H" }); Object.defineProperty(x, "lastName", { value: "Smith", writable: false }); Object.defineProperty(x, "zip", { get() { return 98122 }, set(_) { /*ignore*/ } }); Object.defineProperty(x, "houseNumber", { get() { return 21.75 } }); @@ -34,7 +35,7 @@ var b = x.houseNumber; const returnExemplar = () => x; const needsExemplar = (_ = x) => void 0; -const expected = /** @type {{name: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); +const expected = /** @type {{name: string, readonly middleInit: string, readonly lastName: string, zip: number, readonly houseNumber: number, zipStr: string}} */(/** @type {*} */(null)); /** * @@ -51,15 +52,17 @@ module.exports = x; // Validate in TS as simple validations would usually be interpreted as more special assignments import x = require("./"); x.name; +x.middleInit; x.lastName; x.zip; x.houseNumber; x.zipStr; -x.name = "Another"; +x.name = "Another"; x.zip = 98123; x.zipStr = "OK"; x.lastName = "should fail"; x.houseNumber = 12; // should also fail x.zipStr = 12; // should fail +x.middleInit = "R"; // should also fail From 8da77c620ad27941a71286374e62b6b73408f75a Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 19 Oct 2018 13:11:25 -0700 Subject: [PATCH 4/4] Add test documenting behavior in a few more odd scenarios --- .../checkOtherObjectAssignProperty.errors.txt | 58 ++++++ .../checkOtherObjectAssignProperty.symbols | 121 +++++++++++++ .../checkOtherObjectAssignProperty.types | 170 ++++++++++++++++++ .../jsdoc/checkOtherObjectAssignProperty.ts | 39 ++++ 4 files changed, 388 insertions(+) create mode 100644 tests/baselines/reference/checkOtherObjectAssignProperty.errors.txt create mode 100644 tests/baselines/reference/checkOtherObjectAssignProperty.symbols create mode 100644 tests/baselines/reference/checkOtherObjectAssignProperty.types create mode 100644 tests/cases/conformance/jsdoc/checkOtherObjectAssignProperty.ts diff --git a/tests/baselines/reference/checkOtherObjectAssignProperty.errors.txt b/tests/baselines/reference/checkOtherObjectAssignProperty.errors.txt new file mode 100644 index 0000000000000..146550d6fb62e --- /dev/null +++ b/tests/baselines/reference/checkOtherObjectAssignProperty.errors.txt @@ -0,0 +1,58 @@ +tests/cases/conformance/jsdoc/importer.js(3,5): error TS2339: Property 'other' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. +tests/cases/conformance/jsdoc/importer.js(4,5): error TS2339: Property 'prop' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. +tests/cases/conformance/jsdoc/importer.js(11,5): error TS2339: Property 'other' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. +tests/cases/conformance/jsdoc/importer.js(12,5): error TS2339: Property 'prop' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. +tests/cases/conformance/jsdoc/importer.js(13,5): error TS2540: Cannot assign to 'bad1' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/importer.js(14,5): error TS2540: Cannot assign to 'bad2' because it is a constant or a read-only property. +tests/cases/conformance/jsdoc/importer.js(15,5): error TS2540: Cannot assign to 'bad3' because it is a constant or a read-only property. + + +==== tests/cases/conformance/jsdoc/importer.js (7 errors) ==== + const mod = require("./mod1"); + mod.thing; + mod.other; + ~~~~~ +!!! error TS2339: Property 'other' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. + mod.prop; + ~~~~ +!!! error TS2339: Property 'prop' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. + mod.bad1; + mod.bad2; + mod.bad3; + + + mod.thing = 0; + mod.other = 0; + ~~~~~ +!!! error TS2339: Property 'other' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. + mod.prop = 0; + ~~~~ +!!! error TS2339: Property 'prop' does not exist on type 'typeof import("tests/cases/conformance/jsdoc/mod1")'. + mod.bad1 = 0; + ~~~~ +!!! error TS2540: Cannot assign to 'bad1' because it is a constant or a read-only property. + mod.bad2 = 0; + ~~~~ +!!! error TS2540: Cannot assign to 'bad2' because it is a constant or a read-only property. + mod.bad3 = 0; + ~~~~ +!!! error TS2540: Cannot assign to 'bad3' because it is a constant or a read-only property. + +==== tests/cases/conformance/jsdoc/mod1.js (0 errors) ==== + const obj = { value: 42, writable: true }; + Object.defineProperty(exports, "thing", obj); + + /** + * @type {string} + */ + let str = /** @type {string} */("other"); + Object.defineProperty(exports, str, { value: 42, writable: true }); + + const propName = "prop" + Object.defineProperty(exports, propName, { value: 42, writable: true }); + + + Object.defineProperty(exports, "bad1", { }); + Object.defineProperty(exports, "bad2", { get() { return 12 }, value: "no" }); + Object.defineProperty(exports, "bad3", { writable: true }); + \ No newline at end of file diff --git a/tests/baselines/reference/checkOtherObjectAssignProperty.symbols b/tests/baselines/reference/checkOtherObjectAssignProperty.symbols new file mode 100644 index 0000000000000..efc0a46315602 --- /dev/null +++ b/tests/baselines/reference/checkOtherObjectAssignProperty.symbols @@ -0,0 +1,121 @@ +=== tests/cases/conformance/jsdoc/importer.js === +const mod = require("./mod1"); +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>require : Symbol(require) +>"./mod1" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) + +mod.thing; +>mod.thing : Symbol("thing", Decl(mod1.js, 0, 42)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>thing : Symbol("thing", Decl(mod1.js, 0, 42)) + +mod.other; +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) + +mod.prop; +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) + +mod.bad1; +>mod.bad1 : Symbol("bad1", Decl(mod1.js, 10, 72)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>bad1 : Symbol("bad1", Decl(mod1.js, 10, 72)) + +mod.bad2; +>mod.bad2 : Symbol("bad2", Decl(mod1.js, 13, 44)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>bad2 : Symbol("bad2", Decl(mod1.js, 13, 44)) + +mod.bad3; +>mod.bad3 : Symbol("bad3", Decl(mod1.js, 14, 77)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>bad3 : Symbol("bad3", Decl(mod1.js, 14, 77)) + + +mod.thing = 0; +>mod.thing : Symbol("thing", Decl(mod1.js, 0, 42)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>thing : Symbol("thing", Decl(mod1.js, 0, 42)) + +mod.other = 0; +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) + +mod.prop = 0; +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) + +mod.bad1 = 0; +>mod.bad1 : Symbol("bad1", Decl(mod1.js, 10, 72)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>bad1 : Symbol("bad1", Decl(mod1.js, 10, 72)) + +mod.bad2 = 0; +>mod.bad2 : Symbol("bad2", Decl(mod1.js, 13, 44)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>bad2 : Symbol("bad2", Decl(mod1.js, 13, 44)) + +mod.bad3 = 0; +>mod.bad3 : Symbol("bad3", Decl(mod1.js, 14, 77)) +>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9)) +>bad3 : Symbol("bad3", Decl(mod1.js, 14, 77)) + +=== tests/cases/conformance/jsdoc/mod1.js === +const obj = { value: 42, writable: true }; +>obj : Symbol(obj, Decl(mod1.js, 0, 5)) +>value : Symbol(value, Decl(mod1.js, 0, 13)) +>writable : Symbol(writable, Decl(mod1.js, 0, 24)) + +Object.defineProperty(exports, "thing", obj); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>obj : Symbol(obj, Decl(mod1.js, 0, 5)) + +/** + * @type {string} + */ +let str = /** @type {string} */("other"); +>str : Symbol(str, Decl(mod1.js, 6, 3)) + +Object.defineProperty(exports, str, { value: 42, writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>str : Symbol(str, Decl(mod1.js, 6, 3)) +>value : Symbol(value, Decl(mod1.js, 7, 37)) +>writable : Symbol(writable, Decl(mod1.js, 7, 48)) + +const propName = "prop" +>propName : Symbol(propName, Decl(mod1.js, 9, 5)) + +Object.defineProperty(exports, propName, { value: 42, writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>propName : Symbol(propName, Decl(mod1.js, 9, 5)) +>value : Symbol(value, Decl(mod1.js, 10, 42)) +>writable : Symbol(writable, Decl(mod1.js, 10, 53)) + + +Object.defineProperty(exports, "bad1", { }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) + +Object.defineProperty(exports, "bad2", { get() { return 12 }, value: "no" }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>get : Symbol(get, Decl(mod1.js, 14, 40)) +>value : Symbol(value, Decl(mod1.js, 14, 61)) + +Object.defineProperty(exports, "bad3", { writable: true }); +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>exports : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0)) +>writable : Symbol(writable, Decl(mod1.js, 15, 40)) + diff --git a/tests/baselines/reference/checkOtherObjectAssignProperty.types b/tests/baselines/reference/checkOtherObjectAssignProperty.types new file mode 100644 index 0000000000000..d9b68d71745e8 --- /dev/null +++ b/tests/baselines/reference/checkOtherObjectAssignProperty.types @@ -0,0 +1,170 @@ +=== tests/cases/conformance/jsdoc/importer.js === +const mod = require("./mod1"); +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>require("./mod1") : typeof import("tests/cases/conformance/jsdoc/mod1") +>require : any +>"./mod1" : "./mod1" + +mod.thing; +>mod.thing : number +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>thing : number + +mod.other; +>mod.other : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>other : any + +mod.prop; +>mod.prop : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>prop : any + +mod.bad1; +>mod.bad1 : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>bad1 : any + +mod.bad2; +>mod.bad2 : string +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>bad2 : string + +mod.bad3; +>mod.bad3 : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>bad3 : any + + +mod.thing = 0; +>mod.thing = 0 : 0 +>mod.thing : number +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>thing : number +>0 : 0 + +mod.other = 0; +>mod.other = 0 : 0 +>mod.other : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>other : any +>0 : 0 + +mod.prop = 0; +>mod.prop = 0 : 0 +>mod.prop : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>prop : any +>0 : 0 + +mod.bad1 = 0; +>mod.bad1 = 0 : 0 +>mod.bad1 : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>bad1 : any +>0 : 0 + +mod.bad2 = 0; +>mod.bad2 = 0 : 0 +>mod.bad2 : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>bad2 : any +>0 : 0 + +mod.bad3 = 0; +>mod.bad3 = 0 : 0 +>mod.bad3 : any +>mod : typeof import("tests/cases/conformance/jsdoc/mod1") +>bad3 : any +>0 : 0 + +=== tests/cases/conformance/jsdoc/mod1.js === +const obj = { value: 42, writable: true }; +>obj : { value: number; writable: boolean; } +>{ value: 42, writable: true } : { value: number; writable: boolean; } +>value : number +>42 : 42 +>writable : boolean +>true : true + +Object.defineProperty(exports, "thing", obj); +>Object.defineProperty(exports, "thing", obj) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"thing" : "thing" +>obj : { value: number; writable: boolean; } + +/** + * @type {string} + */ +let str = /** @type {string} */("other"); +>str : string +>("other") : string +>"other" : "other" + +Object.defineProperty(exports, str, { value: 42, writable: true }); +>Object.defineProperty(exports, str, { value: 42, writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>str : string +>{ value: 42, writable: true } : { value: number; writable: true; } +>value : number +>42 : 42 +>writable : true +>true : true + +const propName = "prop" +>propName : "prop" +>"prop" : "prop" + +Object.defineProperty(exports, propName, { value: 42, writable: true }); +>Object.defineProperty(exports, propName, { value: 42, writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>propName : "prop" +>{ value: 42, writable: true } : { value: number; writable: true; } +>value : number +>42 : 42 +>writable : true +>true : true + + +Object.defineProperty(exports, "bad1", { }); +>Object.defineProperty(exports, "bad1", { }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"bad1" : "bad1" +>{ } : {} + +Object.defineProperty(exports, "bad2", { get() { return 12 }, value: "no" }); +>Object.defineProperty(exports, "bad2", { get() { return 12 }, value: "no" }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"bad2" : "bad2" +>{ get() { return 12 }, value: "no" } : { get(): number; value: string; } +>get : () => number +>12 : 12 +>value : string +>"no" : "no" + +Object.defineProperty(exports, "bad3", { writable: true }); +>Object.defineProperty(exports, "bad3", { writable: true }) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>exports : typeof import("tests/cases/conformance/jsdoc/mod1") +>"bad3" : "bad3" +>{ writable: true } : { writable: true; } +>writable : true +>true : true + diff --git a/tests/cases/conformance/jsdoc/checkOtherObjectAssignProperty.ts b/tests/cases/conformance/jsdoc/checkOtherObjectAssignProperty.ts new file mode 100644 index 0000000000000..a7bccc65283a0 --- /dev/null +++ b/tests/cases/conformance/jsdoc/checkOtherObjectAssignProperty.ts @@ -0,0 +1,39 @@ +// @allowJs: true +// @noEmit: true +// @strict: true +// @checkJs: true +// @filename: mod1.js + +const obj = { value: 42, writable: true }; +Object.defineProperty(exports, "thing", obj); + +/** + * @type {string} + */ +let str = /** @type {string} */("other"); +Object.defineProperty(exports, str, { value: 42, writable: true }); + +const propName = "prop" +Object.defineProperty(exports, propName, { value: 42, writable: true }); + + +Object.defineProperty(exports, "bad1", { }); +Object.defineProperty(exports, "bad2", { get() { return 12 }, value: "no" }); +Object.defineProperty(exports, "bad3", { writable: true }); + +// @filename: importer.js +const mod = require("./mod1"); +mod.thing; +mod.other; +mod.prop; +mod.bad1; +mod.bad2; +mod.bad3; + + +mod.thing = 0; +mod.other = 0; +mod.prop = 0; +mod.bad1 = 0; +mod.bad2 = 0; +mod.bad3 = 0;