diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e59641bed412e..944b975b2b0c5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4714,6 +4714,10 @@ namespace ts { // function/class/{} assignments are fresh declarations, not property assignments, so only add prototype assignments const specialDeclaration = getAssignedJavascriptInitializer(symbol.valueDeclaration); if (specialDeclaration) { + const tag = getJSDocTypeTag(specialDeclaration); + if (tag && tag.typeExpression) { + return getTypeFromTypeNode(tag.typeExpression); + } return getWidenedLiteralType(checkExpressionCached(specialDeclaration)); } const types: Type[] = []; @@ -5081,7 +5085,7 @@ namespace ts { } function getJSInitializerType(decl: Node, symbol: Symbol, init: Expression | undefined): Type | undefined { - if (init && isInJavaScriptFile(init) && isObjectLiteralExpression(init)) { + if (init && isInJavaScriptFile(init) && isObjectLiteralExpression(init) && init.properties.length === 0) { const exports = createSymbolTable(); while (isBinaryExpression(decl) || isPropertyAccessExpression(decl)) { const s = getSymbolOfNode(decl); @@ -15786,22 +15790,22 @@ namespace ts { } // In an assignment expression, the right operand is contextually typed by the type of the left operand. - // Don't do this for special property assignments to avoid circularity. + // Don't do this for special property assignments unless there is a type tag on the assignment, to avoid circularity from checking the right operand. function isContextSensitiveAssignment(binaryExpression: BinaryExpression): boolean { const kind = getSpecialPropertyAssignmentKind(binaryExpression); switch (kind) { case SpecialPropertyAssignmentKind.None: return true; case SpecialPropertyAssignmentKind.Property: - // If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration. - // See `bindStaticPropertyAssignment` in `binder.ts`. - return !binaryExpression.left.symbol; case SpecialPropertyAssignmentKind.ExportsProperty: - case SpecialPropertyAssignmentKind.ModuleExports: + case SpecialPropertyAssignmentKind.Prototype: case SpecialPropertyAssignmentKind.PrototypeProperty: + // If `binaryExpression.left` was assigned a symbol, then this is a new declaration; otherwise it is an assignment to an existing declaration. + // See `bindStaticPropertyAssignment` in `binder.ts`. + return !binaryExpression.left.symbol || binaryExpression.left.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.left.symbol.valueDeclaration); case SpecialPropertyAssignmentKind.ThisProperty: - case SpecialPropertyAssignmentKind.Prototype: - return false; + case SpecialPropertyAssignmentKind.ModuleExports: + return !binaryExpression.symbol || binaryExpression.symbol.valueDeclaration && !!getJSDocTypeTag(binaryExpression.symbol.valueDeclaration); default: return Debug.assertNever(kind); } diff --git a/tests/baselines/reference/chainedPrototypeAssignment.types b/tests/baselines/reference/chainedPrototypeAssignment.types index f0d9cc972ef0f..a893ae63022c6 100644 --- a/tests/baselines/reference/chainedPrototypeAssignment.types +++ b/tests/baselines/reference/chainedPrototypeAssignment.types @@ -86,9 +86,9 @@ A.prototype = B.prototype = { >A : typeof A >prototype : { [x: string]: any; m(n: number): number; } >B.prototype = { /** @param {number} n */ m(n) { return n + 1 }} : { [x: string]: any; m(n: number): number; } ->B.prototype : { [x: string]: any; } +>B.prototype : { [x: string]: any; m(n: number): number; } >B : typeof B ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(n: number): number; } >{ /** @param {number} n */ m(n) { return n + 1 }} : { [x: string]: any; m(n: number): number; } /** @param {number} n */ diff --git a/tests/baselines/reference/conflictingCommonJSES2015Exports.types b/tests/baselines/reference/conflictingCommonJSES2015Exports.types index a9dc1ce80bd7c..a6afee35f2c09 100644 --- a/tests/baselines/reference/conflictingCommonJSES2015Exports.types +++ b/tests/baselines/reference/conflictingCommonJSES2015Exports.types @@ -7,11 +7,11 @@ export function abc(a, b, c) { return 5; } >5 : 5 module.exports = { abc }; ->module.exports = { abc } : { [x: string]: any; abc: (a: any, b: any, c: any) => number; } +>module.exports = { abc } : { abc: (a: any, b: any, c: any) => number; } >module.exports : any >module : any >exports : any ->{ abc } : { [x: string]: any; abc: (a: any, b: any, c: any) => number; } +>{ abc } : { abc: (a: any, b: any, c: any) => number; } >abc : (a: any, b: any, c: any) => number === tests/cases/conformance/salsa/use.js === diff --git a/tests/baselines/reference/contextualTypedSpecialAssignment.errors.txt b/tests/baselines/reference/contextualTypedSpecialAssignment.errors.txt new file mode 100644 index 0000000000000..c5851c4614711 --- /dev/null +++ b/tests/baselines/reference/contextualTypedSpecialAssignment.errors.txt @@ -0,0 +1,89 @@ +tests/cases/conformance/salsa/mod.js(5,7): error TS7006: Parameter 'n' implicitly has an 'any' type. +tests/cases/conformance/salsa/test.js(52,7): error TS7006: Parameter 'n' implicitly has an 'any' type. + + +==== tests/cases/conformance/salsa/test.js (1 errors) ==== + /** @typedef {{ + status: 'done' + m(n: number): void + }} DoneStatus */ + + // property assignment + var ns = {} + /** @type {DoneStatus} */ + ns.x = { + status: 'done', + m(n) { } + } + + ns.x = { + status: 'done', + m(n) { } + } + ns.x + + + // this-property assignment + class Thing { + constructor() { + /** @type {DoneStatus} */ + this.s = { + status: 'done', + m(n) { } + } + } + + fail() { + this.s = { + status: 'done', + m(n) { } + } + } + } + + // exports-property assignment + + /** @type {DoneStatus} */ + exports.x = { + status: "done", + m(n) { } + } + exports.x + + /** @type {DoneStatus} contextual typing is allowed, but module.exports.y: any. + Guess it doesn't check the type tag? */ + module.exports.y = { + status: "done", + m(n) { } + ~ +!!! error TS7006: Parameter 'n' implicitly has an 'any' type. + } + module.exports.y + + // prototype-property assignment + /** @type {DoneStatus} */ + Thing.prototype.x = { + status: 'done', + m(n) { } + } + Thing.prototype.x + + // prototype assignment + function F() { + } + /** @type {DoneStatus} */ + F.prototype = { + status: "done", + m(n) { } + } + +==== tests/cases/conformance/salsa/mod.js (1 errors) ==== + // module.exports assignment + /** @type {{ status: 'done', m(n: number): void }} */ + module.exports = { + status: "done", + m(n) { } + ~ +!!! error TS7006: Parameter 'n' implicitly has an 'any' type. + } + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypedSpecialAssignment.symbols b/tests/baselines/reference/contextualTypedSpecialAssignment.symbols new file mode 100644 index 0000000000000..2a8b1650ae7bc --- /dev/null +++ b/tests/baselines/reference/contextualTypedSpecialAssignment.symbols @@ -0,0 +1,173 @@ +=== tests/cases/conformance/salsa/test.js === +/** @typedef {{ + status: 'done' + m(n: number): void +}} DoneStatus */ + +// property assignment +var ns = {} +>ns : Symbol(ns, Decl(test.js, 6, 3)) + +/** @type {DoneStatus} */ +ns.x = { +>ns.x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1)) +>ns : Symbol(ns, Decl(test.js, 6, 3)) +>x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1)) + + status: 'done', +>status : Symbol(status, Decl(test.js, 8, 8)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 9, 19)) +>n : Symbol(n, Decl(test.js, 10, 6)) +} + +ns.x = { +>ns.x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1)) +>ns : Symbol(ns, Decl(test.js, 6, 3)) +>x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1)) + + status: 'done', +>status : Symbol(status, Decl(test.js, 13, 8)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 14, 19)) +>n : Symbol(n, Decl(test.js, 15, 6)) +} +ns.x +>ns.x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1)) +>ns : Symbol(ns, Decl(test.js, 6, 3)) +>x : Symbol(ns.x, Decl(test.js, 6, 11), Decl(test.js, 11, 1)) + + +// this-property assignment +class Thing { +>Thing : Symbol(Thing, Decl(test.js, 17, 4)) + + constructor() { + /** @type {DoneStatus} */ + this.s = { +>this.s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12)) +>this : Symbol(Thing, Decl(test.js, 17, 4)) +>s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12)) + + status: 'done', +>status : Symbol(status, Decl(test.js, 24, 18)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 25, 27)) +>n : Symbol(n, Decl(test.js, 26, 14)) + } + } + + fail() { +>fail : Symbol(Thing.fail, Decl(test.js, 28, 5)) + + this.s = { +>this.s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12)) +>this : Symbol(Thing, Decl(test.js, 17, 4)) +>s : Symbol(Thing.s, Decl(test.js, 22, 19), Decl(test.js, 30, 12)) + + status: 'done', +>status : Symbol(status, Decl(test.js, 31, 18)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 32, 27)) +>n : Symbol(n, Decl(test.js, 33, 14)) + } + } +} + +// exports-property assignment + +/** @type {DoneStatus} */ +exports.x = { +>exports.x : Symbol(x, Decl(test.js, 36, 1)) +>exports : Symbol(x, Decl(test.js, 36, 1)) +>x : Symbol(x, Decl(test.js, 36, 1)) + + status: "done", +>status : Symbol(status, Decl(test.js, 41, 13)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 42, 19)) +>n : Symbol(n, Decl(test.js, 43, 6)) +} +exports.x +>exports.x : Symbol(x, Decl(test.js, 36, 1)) +>exports : Symbol("tests/cases/conformance/salsa/test", Decl(test.js, 0, 0)) +>x : Symbol(x, Decl(test.js, 36, 1)) + +/** @type {DoneStatus} contextual typing is allowed, but module.exports.y: any. +Guess it doesn't check the type tag? */ +module.exports.y = { +>module.exports : Symbol(y, Decl(test.js, 45, 9)) +>module : Symbol(module) +>y : Symbol(y, Decl(test.js, 45, 9)) + + status: "done", +>status : Symbol(status, Decl(test.js, 49, 20)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 50, 19)) +>n : Symbol(n, Decl(test.js, 51, 6)) +} +module.exports.y +>module : Symbol(module) + +// prototype-property assignment +/** @type {DoneStatus} */ +Thing.prototype.x = { +>Thing.prototype.x : Symbol(Thing.x, Decl(test.js, 53, 16)) +>Thing.prototype : Symbol(Thing.x, Decl(test.js, 53, 16)) +>Thing : Symbol(Thing, Decl(test.js, 17, 4)) +>prototype : Symbol(Thing.prototype) +>x : Symbol(Thing.x, Decl(test.js, 53, 16)) + + status: 'done', +>status : Symbol(status, Decl(test.js, 57, 21)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 58, 19)) +>n : Symbol(n, Decl(test.js, 59, 6)) +} +Thing.prototype.x +>Thing.prototype.x : Symbol(Thing.x, Decl(test.js, 53, 16)) +>Thing.prototype : Symbol(Thing.prototype) +>Thing : Symbol(Thing, Decl(test.js, 17, 4)) +>prototype : Symbol(Thing.prototype) +>x : Symbol(Thing.x, Decl(test.js, 53, 16)) + +// prototype assignment +function F() { +>F : Symbol(F, Decl(test.js, 61, 17), Decl(test.js, 65, 1)) +} +/** @type {DoneStatus} */ +F.prototype = { +>F.prototype : Symbol(F.prototype, Decl(test.js, 65, 1)) +>F : Symbol(F, Decl(test.js, 61, 17), Decl(test.js, 65, 1)) +>prototype : Symbol(F.prototype, Decl(test.js, 65, 1)) + + status: "done", +>status : Symbol(status, Decl(test.js, 67, 15)) + + m(n) { } +>m : Symbol(m, Decl(test.js, 68, 19)) +>n : Symbol(n, Decl(test.js, 69, 6)) +} + +=== tests/cases/conformance/salsa/mod.js === +// module.exports assignment +/** @type {{ status: 'done', m(n: number): void }} */ +module.exports = { +>module : Symbol(export=, Decl(mod.js, 0, 0)) +>exports : Symbol(export=, Decl(mod.js, 0, 0)) + + status: "done", +>status : Symbol(status, Decl(mod.js, 2, 18)) + + m(n) { } +>m : Symbol(m, Decl(mod.js, 3, 19)) +>n : Symbol(n, Decl(mod.js, 4, 6)) +} + diff --git a/tests/baselines/reference/contextualTypedSpecialAssignment.types b/tests/baselines/reference/contextualTypedSpecialAssignment.types new file mode 100644 index 0000000000000..5bd81e65283e4 --- /dev/null +++ b/tests/baselines/reference/contextualTypedSpecialAssignment.types @@ -0,0 +1,208 @@ +=== tests/cases/conformance/salsa/test.js === +/** @typedef {{ + status: 'done' + m(n: number): void +}} DoneStatus */ + +// property assignment +var ns = {} +>ns : { [x: string]: any; x: { status: "done"; m(n: number): void; }; } +>{} : { [x: string]: any; } + +/** @type {DoneStatus} */ +ns.x = { +>ns.x = { status: 'done', m(n) { }} : { status: "done"; m(n: number): void; } +>ns.x : { status: "done"; m(n: number): void; } +>ns : { [x: string]: any; x: { status: "done"; m(n: number): void; }; } +>x : { status: "done"; m(n: number): void; } +>{ status: 'done', m(n) { }} : { status: "done"; m(n: number): void; } + + status: 'done', +>status : "done" +>'done' : "done" + + m(n) { } +>m : (n: number) => void +>n : number +} + +ns.x = { +>ns.x = { status: 'done', m(n) { }} : { status: "done"; m(n: number): void; } +>ns.x : { status: "done"; m(n: number): void; } +>ns : { [x: string]: any; x: { status: "done"; m(n: number): void; }; } +>x : { status: "done"; m(n: number): void; } +>{ status: 'done', m(n) { }} : { status: "done"; m(n: number): void; } + + status: 'done', +>status : "done" +>'done' : "done" + + m(n) { } +>m : (n: number) => void +>n : number +} +ns.x +>ns.x : { status: "done"; m(n: number): void; } +>ns : { [x: string]: any; x: { status: "done"; m(n: number): void; }; } +>x : { status: "done"; m(n: number): void; } + + +// this-property assignment +class Thing { +>Thing : Thing + + constructor() { + /** @type {DoneStatus} */ + this.s = { +>this.s = { status: 'done', m(n) { } } : { status: "done"; m(n: number): void; } +>this.s : { status: "done"; m(n: number): void; } +>this : this +>s : { status: "done"; m(n: number): void; } +>{ status: 'done', m(n) { } } : { status: "done"; m(n: number): void; } + + status: 'done', +>status : "done" +>'done' : "done" + + m(n) { } +>m : (n: number) => void +>n : number + } + } + + fail() { +>fail : () => void + + this.s = { +>this.s = { status: 'done', m(n) { } } : { status: "done"; m(n: number): void; } +>this.s : { status: "done"; m(n: number): void; } +>this : this +>s : { status: "done"; m(n: number): void; } +>{ status: 'done', m(n) { } } : { status: "done"; m(n: number): void; } + + status: 'done', +>status : "done" +>'done' : "done" + + m(n) { } +>m : (n: number) => void +>n : number + } + } +} + +// exports-property assignment + +/** @type {DoneStatus} */ +exports.x = { +>exports.x = { status: "done", m(n) { }} : { status: "done"; m(n: number): void; } +>exports.x : { status: "done"; m(n: number): void; } +>exports : typeof import("tests/cases/conformance/salsa/test") +>x : { status: "done"; m(n: number): void; } +>{ status: "done", m(n) { }} : { status: "done"; m(n: number): void; } + + status: "done", +>status : "done" +>"done" : "done" + + m(n) { } +>m : (n: number) => void +>n : number +} +exports.x +>exports.x : { status: "done"; m(n: number): void; } +>exports : typeof import("tests/cases/conformance/salsa/test") +>x : { status: "done"; m(n: number): void; } + +/** @type {DoneStatus} contextual typing is allowed, but module.exports.y: any. +Guess it doesn't check the type tag? */ +module.exports.y = { +>module.exports.y = { status: "done", m(n) { }} : { status: string; m(n: any): void; } +>module.exports.y : any +>module.exports : any +>module : any +>exports : any +>y : any +>{ status: "done", m(n) { }} : { status: string; m(n: any): void; } + + status: "done", +>status : string +>"done" : "done" + + m(n) { } +>m : (n: any) => void +>n : any +} +module.exports.y +>module.exports.y : any +>module.exports : any +>module : any +>exports : any +>y : any + +// prototype-property assignment +/** @type {DoneStatus} */ +Thing.prototype.x = { +>Thing.prototype.x = { status: 'done', m(n) { }} : { status: "done"; m(n: number): void; } +>Thing.prototype.x : { status: "done"; m(n: number): void; } +>Thing.prototype : Thing +>Thing : typeof Thing +>prototype : Thing +>x : { status: "done"; m(n: number): void; } +>{ status: 'done', m(n) { }} : { status: "done"; m(n: number): void; } + + status: 'done', +>status : "done" +>'done' : "done" + + m(n) { } +>m : (n: number) => void +>n : number +} +Thing.prototype.x +>Thing.prototype.x : { status: "done"; m(n: number): void; } +>Thing.prototype : Thing +>Thing : typeof Thing +>prototype : Thing +>x : { status: "done"; m(n: number): void; } + +// prototype assignment +function F() { +>F : typeof F +} +/** @type {DoneStatus} */ +F.prototype = { +>F.prototype = { status: "done", m(n) { }} : { status: "done"; m(n: number): void; } +>F.prototype : { status: "done"; m(n: number): void; } +>F : typeof F +>prototype : { status: "done"; m(n: number): void; } +>{ status: "done", m(n) { }} : { status: "done"; m(n: number): void; } + + status: "done", +>status : "done" +>"done" : "done" + + m(n) { } +>m : (n: number) => void +>n : number +} + +=== tests/cases/conformance/salsa/mod.js === +// module.exports assignment +/** @type {{ status: 'done', m(n: number): void }} */ +module.exports = { +>module.exports = { status: "done", m(n) { }} : { status: string; m(n: any): void; } +>module.exports : any +>module : any +>exports : any +>{ status: "done", m(n) { }} : { status: string; m(n: any): void; } + + status: "done", +>status : string +>"done" : "done" + + m(n) { } +>m : (n: any) => void +>n : any +} + diff --git a/tests/baselines/reference/moduleExportAlias.types b/tests/baselines/reference/moduleExportAlias.types index 924f02d7fa866..707dc90a1f5c7 100644 --- a/tests/baselines/reference/moduleExportAlias.types +++ b/tests/baselines/reference/moduleExportAlias.types @@ -223,19 +223,19 @@ multipleDeclarationAlias5.func9 = function () { }; >function () { } : () => void var multipleDeclarationAlias6 = exports = module.exports = {}; ->multipleDeclarationAlias6 : { [x: string]: any; } ->exports = module.exports = {} : { [x: string]: any; } +>multipleDeclarationAlias6 : {} +>exports = module.exports = {} : {} >exports : typeof import("tests/cases/conformance/salsa/b") ->module.exports = {} : { [x: string]: any; } +>module.exports = {} : {} >module.exports : any >module : any >exports : any ->{} : { [x: string]: any; } +>{} : {} multipleDeclarationAlias6.func10 = function () { }; >multipleDeclarationAlias6.func10 = function () { } : () => void >multipleDeclarationAlias6.func10 : any ->multipleDeclarationAlias6 : { [x: string]: any; } +>multipleDeclarationAlias6 : {} >func10 : any >function () { } : () => void @@ -294,13 +294,13 @@ module.exports.func12 = function () { }; >function () { } : () => void exports = module.exports = {}; ->exports = module.exports = {} : { [x: string]: any; } +>exports = module.exports = {} : {} >exports : typeof import("tests/cases/conformance/salsa/b") ->module.exports = {} : { [x: string]: any; } +>module.exports = {} : {} >module.exports : any >module : any >exports : any ->{} : { [x: string]: any; } +>{} : {} exports.func13 = function () { }; >exports.func13 = function () { } : () => void @@ -319,13 +319,13 @@ module.exports.func14 = function () { }; >function () { } : () => void exports = module.exports = {}; ->exports = module.exports = {} : { [x: string]: any; } +>exports = module.exports = {} : {} >exports : typeof import("tests/cases/conformance/salsa/b") ->module.exports = {} : { [x: string]: any; } +>module.exports = {} : {} >module.exports : any >module : any >exports : any ->{} : { [x: string]: any; } +>{} : {} exports.func15 = function () { }; >exports.func15 = function () { } : () => void @@ -369,11 +369,11 @@ module.exports.func18 = function () { }; >function () { } : () => void module.exports = {}; ->module.exports = {} : { [x: string]: any; } +>module.exports = {} : {} >module.exports : any >module : any >exports : any ->{} : { [x: string]: any; } +>{} : {} exports.func19 = function () { }; >exports.func19 = function () { } : () => void diff --git a/tests/baselines/reference/typeFromPropertyAssignment11.types b/tests/baselines/reference/typeFromPropertyAssignment11.types index 87a709d7cf3a4..8c90e824f23c5 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment11.types +++ b/tests/baselines/reference/typeFromPropertyAssignment11.types @@ -5,9 +5,9 @@ var Inner = function() {} Inner.prototype = { >Inner.prototype = { m() { }, i: 1} : { [x: string]: any; m(): void; i: number; } ->Inner.prototype : { [x: string]: any; } +>Inner.prototype : { [x: string]: any; m(): void; i: number; } >Inner : typeof Inner ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(): void; i: number; } >{ m() { }, i: 1} : { [x: string]: any; m(): void; i: number; } m() { }, @@ -21,18 +21,18 @@ Inner.prototype = { Inner.prototype.j = 2 >Inner.prototype.j = 2 : 2 >Inner.prototype.j : any ->Inner.prototype : { [x: string]: any; } +>Inner.prototype : { [x: string]: any; m(): void; i: number; } >Inner : typeof Inner ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(): void; i: number; } >j : any >2 : 2 /** @type {string} */ Inner.prototype.k; >Inner.prototype.k : any ->Inner.prototype : { [x: string]: any; } +>Inner.prototype : { [x: string]: any; m(): void; i: number; } >Inner : typeof Inner ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(): void; i: number; } >k : any var inner = new Inner() diff --git a/tests/baselines/reference/typeFromPropertyAssignment13.types b/tests/baselines/reference/typeFromPropertyAssignment13.types index 5b97d2986f57c..f85325a60a22c 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment13.types +++ b/tests/baselines/reference/typeFromPropertyAssignment13.types @@ -12,11 +12,11 @@ Outer.Inner = function() {} Outer.Inner.prototype = { >Outer.Inner.prototype = { m() { }, i: 1} : { [x: string]: any; m(): void; i: number; } ->Outer.Inner.prototype : { [x: string]: any; } +>Outer.Inner.prototype : { [x: string]: any; m(): void; i: number; } >Outer.Inner : typeof Inner >Outer : typeof Outer >Inner : typeof Inner ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(): void; i: number; } >{ m() { }, i: 1} : { [x: string]: any; m(): void; i: number; } m() { }, @@ -30,22 +30,22 @@ Outer.Inner.prototype = { Outer.Inner.prototype.j = 2 >Outer.Inner.prototype.j = 2 : 2 >Outer.Inner.prototype.j : any ->Outer.Inner.prototype : { [x: string]: any; } +>Outer.Inner.prototype : { [x: string]: any; m(): void; i: number; } >Outer.Inner : typeof Inner >Outer : typeof Outer >Inner : typeof Inner ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(): void; i: number; } >j : any >2 : 2 /** @type {string} */ Outer.Inner.prototype.k; >Outer.Inner.prototype.k : any ->Outer.Inner.prototype : { [x: string]: any; } +>Outer.Inner.prototype : { [x: string]: any; m(): void; i: number; } >Outer.Inner : typeof Inner >Outer : typeof Outer >Inner : typeof Inner ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; m(): void; i: number; } >k : any var inner = new Outer.Inner() diff --git a/tests/baselines/reference/typeFromPropertyAssignment14.types b/tests/baselines/reference/typeFromPropertyAssignment14.types index 60d63c489d6b3..a20a5a15fc3c6 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment14.types +++ b/tests/baselines/reference/typeFromPropertyAssignment14.types @@ -5,19 +5,19 @@ var Outer = {}; === tests/cases/conformance/salsa/work.js === Outer.Inner = function () {} ->Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; }; } ->Outer.Inner : { (): void; prototype: { [x: string]: any; }; } +>Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } +>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } >Outer : typeof Outer ->Inner : { (): void; prototype: { [x: string]: any; }; } ->function () {} : { (): void; prototype: { [x: string]: any; }; } +>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } +>function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } Outer.Inner.prototype = { >Outer.Inner.prototype = { x: 1, m() { }} : { [x: string]: any; x: number; m(): void; } ->Outer.Inner.prototype : { [x: string]: any; } ->Outer.Inner : { (): void; prototype: { [x: string]: any; }; } +>Outer.Inner.prototype : { [x: string]: any; x: number; m(): void; } +>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } >Outer : typeof Outer ->Inner : { (): void; prototype: { [x: string]: any; }; } ->prototype : { [x: string]: any; } +>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } +>prototype : { [x: string]: any; x: number; m(): void; } >{ x: 1, m() { }} : { [x: string]: any; x: number; m(): void; } x: 1, @@ -47,9 +47,9 @@ inner.m() var inno = new Outer.Inner() >inno : { [x: string]: any; x: number; m(): void; } >new Outer.Inner() : { [x: string]: any; x: number; m(): void; } ->Outer.Inner : { (): void; prototype: { [x: string]: any; }; } +>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } >Outer : typeof Outer ->Inner : { (): void; prototype: { [x: string]: any; }; } +>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } inno.x >inno.x : number diff --git a/tests/baselines/reference/typeFromPropertyAssignment16.types b/tests/baselines/reference/typeFromPropertyAssignment16.types index d67d6ccca3763..ce0ae3ae16e23 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment16.types +++ b/tests/baselines/reference/typeFromPropertyAssignment16.types @@ -4,19 +4,19 @@ var Outer = {}; >{} : { [x: string]: any; } Outer.Inner = function () {} ->Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; }; } ->Outer.Inner : { (): void; prototype: { [x: string]: any; }; } +>Outer.Inner = function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } +>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } >Outer : typeof Outer ->Inner : { (): void; prototype: { [x: string]: any; }; } ->function () {} : { (): void; prototype: { [x: string]: any; }; } +>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } +>function () {} : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } Outer.Inner.prototype = { >Outer.Inner.prototype = { x: 1, m() { }} : { [x: string]: any; x: number; m(): void; } ->Outer.Inner.prototype : { [x: string]: any; } ->Outer.Inner : { (): void; prototype: { [x: string]: any; }; } +>Outer.Inner.prototype : { [x: string]: any; x: number; m(): void; } +>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } >Outer : typeof Outer ->Inner : { (): void; prototype: { [x: string]: any; }; } ->prototype : { [x: string]: any; } +>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } +>prototype : { [x: string]: any; x: number; m(): void; } >{ x: 1, m() { }} : { [x: string]: any; x: number; m(): void; } x: 1, @@ -45,9 +45,9 @@ inner.m() var inno = new Outer.Inner() >inno : { [x: string]: any; x: number; m(): void; } >new Outer.Inner() : { [x: string]: any; x: number; m(): void; } ->Outer.Inner : { (): void; prototype: { [x: string]: any; }; } +>Outer.Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } >Outer : typeof Outer ->Inner : { (): void; prototype: { [x: string]: any; }; } +>Inner : { (): void; prototype: { [x: string]: any; x: number; m(): void; }; } inno.x >inno.x : number diff --git a/tests/baselines/reference/typeFromPropertyAssignment27.types b/tests/baselines/reference/typeFromPropertyAssignment27.types index 222bd04e74d14..dcaefe7a95575 100644 --- a/tests/baselines/reference/typeFromPropertyAssignment27.types +++ b/tests/baselines/reference/typeFromPropertyAssignment27.types @@ -10,9 +10,9 @@ function C() { this.p = 1; } C.prototype = { q: 2 }; >C.prototype = { q: 2 } : { [x: string]: any; q: number; } ->C.prototype : { [x: string]: any; } +>C.prototype : { [x: string]: any; q: number; } >C : typeof C ->prototype : { [x: string]: any; } +>prototype : { [x: string]: any; q: number; } >{ q: 2 } : { [x: string]: any; q: number; } >q : number >2 : 2 diff --git a/tests/cases/conformance/salsa/contextualTypedSpecialAssignment.ts b/tests/cases/conformance/salsa/contextualTypedSpecialAssignment.ts new file mode 100644 index 0000000000000..e4a2b56970c1b --- /dev/null +++ b/tests/cases/conformance/salsa/contextualTypedSpecialAssignment.ts @@ -0,0 +1,84 @@ +// @checkJs: true +// @allowJs: true +// @noEmit: true +// @Filename: test.js +// @strict: true +/** @typedef {{ + status: 'done' + m(n: number): void +}} DoneStatus */ + +// property assignment +var ns = {} +/** @type {DoneStatus} */ +ns.x = { + status: 'done', + m(n) { } +} + +ns.x = { + status: 'done', + m(n) { } +} +ns.x + + +// this-property assignment +class Thing { + constructor() { + /** @type {DoneStatus} */ + this.s = { + status: 'done', + m(n) { } + } + } + + fail() { + this.s = { + status: 'done', + m(n) { } + } + } +} + +// exports-property assignment + +/** @type {DoneStatus} */ +exports.x = { + status: "done", + m(n) { } +} +exports.x + +/** @type {DoneStatus} contextual typing is allowed, but module.exports.y: any. +Guess it doesn't check the type tag? */ +module.exports.y = { + status: "done", + m(n) { } +} +module.exports.y + +// prototype-property assignment +/** @type {DoneStatus} */ +Thing.prototype.x = { + status: 'done', + m(n) { } +} +Thing.prototype.x + +// prototype assignment +function F() { +} +/** @type {DoneStatus} */ +F.prototype = { + status: "done", + m(n) { } +} + +// @Filename: mod.js +// module.exports assignment +/** @type {{ status: 'done', m(n: number): void }} */ +module.exports = { + status: "done", + m(n) { } +}