Explicitly typed prototype assignments are context sensitive (#25688)

* Explicitly typed prototype assignments:ctx sensitive

Follow up to #25619: Add the necessary code to type `prototype`
correctly in prototype assignments so that code like
`F.prototype = { ... }` properly makes the object literal context
sensitive.

* Fix lint
This commit is contained in:
Nathan Shively-Sanders 2018-07-16 10:03:39 -07:00 committed by GitHub
parent 60986adee5
commit 5b21cbc0c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 53 additions and 52 deletions

View file

@ -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);

View file

@ -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 */

View file

@ -1,9 +1,8 @@
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(70,7): error TS7006: Parameter 'n' implicitly has an 'any' type.
==== tests/cases/conformance/salsa/test.js (2 errors) ====
==== tests/cases/conformance/salsa/test.js (1 errors) ====
/** @typedef {{
status: 'done'
m(n: number): void
@ -76,13 +75,11 @@ tests/cases/conformance/salsa/test.js(70,7): error TS7006: Parameter 'n' implici
F.prototype = {
status: "done",
m(n) { }
~
!!! error TS7006: Parameter 'n' implicitly has an 'any' type.
}
==== tests/cases/conformance/salsa/mod.js (1 errors) ====
// module.exports assignment
/** @type {{ status: 'done' }} */
/** @type {{ status: 'done', m(n: number): void }} */
module.exports = {
status: "done",
m(n) { }

View file

@ -158,7 +158,7 @@ F.prototype = {
=== tests/cases/conformance/salsa/mod.js ===
// module.exports assignment
/** @type {{ status: 'done' }} */
/** @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))

View file

@ -172,24 +172,24 @@ function F() {
}
/** @type {DoneStatus} */
F.prototype = {
>F.prototype = { status: "done", m(n) { }} : { status: string; m(n: any): void; }
>F.prototype : { [x: string]: any; }
>F.prototype = { status: "done", m(n) { }} : { status: "done"; m(n: number): void; }
>F.prototype : { status: "done"; m(n: number): void; }
>F : typeof F
>prototype : { [x: string]: any; }
>{ status: "done", m(n) { }} : { status: string; m(n: any): void; }
>prototype : { status: "done"; m(n: number): void; }
>{ status: "done", m(n) { }} : { status: "done"; m(n: number): void; }
status: "done",
>status : string
>status : "done"
>"done" : "done"
m(n) { }
>m : (n: any) => void
>n : any
>m : (n: number) => void
>n : number
}
=== tests/cases/conformance/salsa/mod.js ===
// module.exports assignment
/** @type {{ status: 'done' }} */
/** @type {{ status: 'done', m(n: number): void }} */
module.exports = {
>module.exports = { status: "done", m(n) { }} : { status: string; m(n: any): void; }
>module.exports : any

View file

@ -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()

View file

@ -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()

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -77,7 +77,7 @@ F.prototype = {
// @Filename: mod.js
// module.exports assignment
/** @type {{ status: 'done' }} */
/** @type {{ status: 'done', m(n: number): void }} */
module.exports = {
status: "done",
m(n) { }