Incremental prototype+prototype assignment work
Had to fix nested incremental prototype detection, so I'll probably merge this branch back into the PR branch.
This commit is contained in:
parent
b14cf4ef9a
commit
41fba6f34b
|
@ -2367,6 +2367,7 @@ namespace ts {
|
|||
/** For `x.prototype = { p, ... }`, declare members p,... if `x` is function/class/{}, or not declared. */
|
||||
function bindPrototypeAssignment(node: BinaryExpression) {
|
||||
node.left.parent = node;
|
||||
node.right.parent = node;
|
||||
bindPropertyAssignment(node.left as PropertyAccessEntityNameExpression, node.left as PropertyAccessEntityNameExpression, /*isPrototypeProperty*/ false);
|
||||
}
|
||||
|
||||
|
@ -2416,7 +2417,7 @@ namespace ts {
|
|||
function bindPropertyAssignment(name: EntityNameExpression, propertyAccess: PropertyAccessEntityNameExpression, isPrototypeProperty: boolean) {
|
||||
let symbol = getJSInitializerSymbol(lookupSymbolForPropertyAccess(name));
|
||||
const isToplevelNamespaceableInitializer = isBinaryExpression(propertyAccess.parent) ?
|
||||
propertyAccess.parent.parent.parent.kind === SyntaxKind.SourceFile && (getJavascriptInitializer(propertyAccess.parent.right) || isJavascriptPrototypeAssignment(propertyAccess.parent)) :
|
||||
propertyAccess.parent.parent.parent.kind === SyntaxKind.SourceFile && getJavascriptInitializer(propertyAccess.parent.right) :
|
||||
propertyAccess.parent.parent.kind === SyntaxKind.SourceFile;
|
||||
if (!isPrototypeProperty && (!symbol || !(symbol.flags & SymbolFlags.Namespace)) && isToplevelNamespaceableInitializer) {
|
||||
// make symbols or add declarations for intermediate containers
|
||||
|
|
|
@ -17895,32 +17895,33 @@ namespace ts {
|
|||
|
||||
function getJavaScriptClassType(symbol: Symbol): Type | undefined {
|
||||
if (isDeclarationOfFunctionOrClassExpression(symbol)) {
|
||||
symbol = getSymbolOfNode((<VariableDeclaration>symbol.valueDeclaration).initializer);
|
||||
symbol = getSymbolOfNode((symbol.valueDeclaration as VariableDeclaration).initializer);
|
||||
}
|
||||
// TODO: Could stick members on this somehow
|
||||
let assigned = getAssignedClassType(symbol);
|
||||
let inferred: Type | undefined;
|
||||
if (isJavaScriptConstructor(symbol.valueDeclaration)) {
|
||||
return getInferredClassType(symbol);
|
||||
}
|
||||
// // OR: Get them another way
|
||||
const otherSymbol: Symbol = getOtherSymbol(symbol.valueDeclaration);
|
||||
if (otherSymbol) {
|
||||
const prototype = forEach(otherSymbol.declarations, d => getAssignedJavascriptPrototype(d.parent));
|
||||
if (prototype) {
|
||||
// NOTE: Should be able to check the original symbol for other prototype declarations, right?
|
||||
// Not sure why not. Maybe intersecting them would work then.
|
||||
return checkExpression(prototype);
|
||||
}
|
||||
inferred = getInferredClassType(symbol);
|
||||
}
|
||||
if (symbol.flags & SymbolFlags.Variable) {
|
||||
const valueType = getTypeOfSymbol(symbol);
|
||||
if (valueType.symbol && !isInferredClassType(valueType) && isJavaScriptConstructor(valueType.symbol.valueDeclaration)) {
|
||||
return getInferredClassType(valueType.symbol);
|
||||
inferred = getInferredClassType(valueType.symbol);
|
||||
}
|
||||
}
|
||||
return !inferred ? assigned :
|
||||
!assigned ? inferred :
|
||||
getIntersectionType([inferred, assigned]);
|
||||
}
|
||||
|
||||
function getOtherSymbol(node: Node) {
|
||||
return node && node.parent && isBinaryExpression(node.parent) && getSymbolOfNode(node.parent.left);
|
||||
function getAssignedClassType(symbol: Symbol) {
|
||||
const decl = symbol.valueDeclaration;
|
||||
const assignmentSymbol = decl && decl.parent && isBinaryExpression(decl.parent) && getSymbolOfNode(decl.parent.left);
|
||||
if (assignmentSymbol) {
|
||||
const prototype = forEach(assignmentSymbol.declarations, d => getAssignedJavascriptPrototype(d.parent));
|
||||
if (prototype) {
|
||||
return checkExpression(prototype);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getInferredClassType(symbol: Symbol) {
|
||||
|
|
|
@ -1483,12 +1483,6 @@ namespace ts {
|
|||
return false;
|
||||
}
|
||||
|
||||
export function isJavascriptPrototypeAssignment(e: BinaryExpression) {
|
||||
return isObjectLiteralExpression(e.right) &&
|
||||
isPropertyAccessExpression(e.left) &&
|
||||
e.left.name.escapedText === "prototype";
|
||||
}
|
||||
|
||||
export function getJSInitializerSymbol(symbol: Symbol) {
|
||||
if (!symbol || !symbol.valueDeclaration) {
|
||||
return symbol;
|
||||
|
@ -1525,9 +1519,12 @@ namespace ts {
|
|||
const e = skipParentheses(initializer.expression);
|
||||
return e.kind === SyntaxKind.FunctionExpression || e.kind === SyntaxKind.ArrowFunction ? initializer : undefined;
|
||||
}
|
||||
if (initializer.kind === SyntaxKind.FunctionExpression ||
|
||||
initializer.kind === SyntaxKind.ClassExpression ||
|
||||
isObjectLiteralExpression(initializer) && initializer.properties.length === 0) {
|
||||
if (initializer.kind === SyntaxKind.FunctionExpression || initializer.kind === SyntaxKind.ClassExpression) {
|
||||
return initializer;
|
||||
}
|
||||
if (isObjectLiteralExpression(initializer) &&
|
||||
(initializer.properties.length === 0 ||
|
||||
isBinaryExpression(initializer.parent) && isPropertyAccessExpression(initializer.parent.left) && initializer.parent.left.name.escapedText === "prototype")) {
|
||||
return initializer;
|
||||
}
|
||||
}
|
||||
|
@ -1599,32 +1596,38 @@ namespace ts {
|
|||
// module.exports = expr
|
||||
return SpecialPropertyAssignmentKind.ModuleExports;
|
||||
}
|
||||
// TODO: Can probably unify these checks with those in the second half
|
||||
else if (lhs.name.escapedText === "prototype") {
|
||||
return SpecialPropertyAssignmentKind.Prototype;
|
||||
}
|
||||
else {
|
||||
// F.x = expr
|
||||
return SpecialPropertyAssignmentKind.Property;
|
||||
}
|
||||
}
|
||||
else if (lhs.name.escapedText === "prototype" && expr.right.kind === SyntaxKind.ObjectLiteralExpression) {
|
||||
// F.prototype = { ... }
|
||||
return SpecialPropertyAssignmentKind.Prototype;
|
||||
}
|
||||
else if (lhs.expression.kind === SyntaxKind.ThisKeyword) {
|
||||
return SpecialPropertyAssignmentKind.ThisProperty;
|
||||
}
|
||||
else if (isPropertyAccessExpression(lhs.expression)) {
|
||||
// chained dot, e.g. x.y.z = expr; this var is the 'x.y' part
|
||||
if (isIdentifier(lhs.expression.expression)) {
|
||||
// module.exports.name = expr
|
||||
if (lhs.expression.expression.escapedText === "module" && lhs.expression.name.escapedText === "exports") {
|
||||
else if (isEntityNameExpression(lhs.expression)) {
|
||||
if (lhs.name.escapedText === "prototype" && isObjectLiteralExpression(expr.right)) {
|
||||
// F.prototype = { ... }
|
||||
return SpecialPropertyAssignmentKind.Prototype;
|
||||
}
|
||||
else if (isPropertyAccessExpression(lhs.expression)) {
|
||||
// chained dot, e.g. x.y.z = expr; this var is the 'x.y' part
|
||||
if (isIdentifier(lhs.expression.expression) &&
|
||||
lhs.expression.expression.escapedText === "module" &&
|
||||
lhs.expression.name.escapedText === "exports") {
|
||||
// module.exports.name = expr
|
||||
return SpecialPropertyAssignmentKind.ExportsProperty;
|
||||
}
|
||||
if (lhs.expression.name.escapedText === "prototype") {
|
||||
// F.G....prototype.x = expr
|
||||
return SpecialPropertyAssignmentKind.PrototypeProperty;
|
||||
}
|
||||
}
|
||||
if (isEntityNameExpression(lhs.expression)) {
|
||||
return SpecialPropertyAssignmentKind.Property;
|
||||
}
|
||||
// F.G...x = expr
|
||||
return SpecialPropertyAssignmentKind.Property;
|
||||
}
|
||||
|
||||
return SpecialPropertyAssignmentKind.None;
|
||||
|
|
|
@ -20,8 +20,17 @@ Outer.Inner.prototype = {
|
|||
i: 1
|
||||
>i : Symbol(i, Decl(module.js, 3, 12))
|
||||
}
|
||||
// NOTE: incremental assignments don't work (but don't need to for chrome at least)
|
||||
// incremental assignments still work
|
||||
Outer.Inner.prototype.j = 2
|
||||
>Outer.Inner.prototype : Symbol((Anonymous function).j, Decl(module.js, 5, 1))
|
||||
>Outer.Inner : Symbol(Inner, Decl(module.js, 0, 14), Decl(module.js, 2, 6))
|
||||
>Outer : Symbol(Outer, Decl(module.js, 0, 3), Decl(module.js, 0, 14), Decl(module.js, 1, 27))
|
||||
>Inner : Symbol(Inner, Decl(module.js, 0, 14), Decl(module.js, 2, 6))
|
||||
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
|
||||
>j : Symbol((Anonymous function).j, Decl(module.js, 5, 1))
|
||||
|
||||
/** @type {string} */
|
||||
Outer.Inner.prototype.k;
|
||||
>Outer.Inner.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
|
||||
>Outer.Inner : Symbol(Inner, Decl(module.js, 0, 14), Decl(module.js, 2, 6))
|
||||
>Outer : Symbol(Outer, Decl(module.js, 0, 3), Decl(module.js, 0, 14), Decl(module.js, 1, 27))
|
||||
|
@ -29,21 +38,28 @@ Outer.Inner.prototype.j = 2
|
|||
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
var inner = new Outer.Inner()
|
||||
>inner : Symbol(inner, Decl(module.js, 8, 3))
|
||||
>inner : Symbol(inner, Decl(module.js, 10, 3))
|
||||
>Outer.Inner : Symbol(Inner, Decl(module.js, 0, 14), Decl(module.js, 2, 6))
|
||||
>Outer : Symbol(Outer, Decl(module.js, 0, 3), Decl(module.js, 0, 14), Decl(module.js, 1, 27))
|
||||
>Inner : Symbol(Inner, Decl(module.js, 0, 14), Decl(module.js, 2, 6))
|
||||
|
||||
inner.m()
|
||||
>inner.m : Symbol(m, Decl(module.js, 2, 25))
|
||||
>inner : Symbol(inner, Decl(module.js, 8, 3))
|
||||
>inner : Symbol(inner, Decl(module.js, 10, 3))
|
||||
>m : Symbol(m, Decl(module.js, 2, 25))
|
||||
|
||||
inner.i
|
||||
>inner.i : Symbol(i, Decl(module.js, 3, 12))
|
||||
>inner : Symbol(inner, Decl(module.js, 8, 3))
|
||||
>inner : Symbol(inner, Decl(module.js, 10, 3))
|
||||
>i : Symbol(i, Decl(module.js, 3, 12))
|
||||
|
||||
inner.j
|
||||
>inner : Symbol(inner, Decl(module.js, 8, 3))
|
||||
>inner.j : Symbol((Anonymous function).j, Decl(module.js, 5, 1))
|
||||
>inner : Symbol(inner, Decl(module.js, 10, 3))
|
||||
>j : Symbol((Anonymous function).j, Decl(module.js, 5, 1))
|
||||
|
||||
inner.k
|
||||
>inner.k : Symbol((Anonymous function).k, Decl(module.js, 7, 27))
|
||||
>inner : Symbol(inner, Decl(module.js, 10, 3))
|
||||
>k : Symbol((Anonymous function).k, Decl(module.js, 7, 27))
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ Outer.Inner.prototype = {
|
|||
>i : number
|
||||
>1 : 1
|
||||
}
|
||||
// NOTE: incremental assignments don't work (but don't need to for chrome at least)
|
||||
// incremental assignments still work
|
||||
Outer.Inner.prototype.j = 2
|
||||
>Outer.Inner.prototype.j = 2 : 2
|
||||
>Outer.Inner.prototype.j : any
|
||||
|
@ -38,9 +38,19 @@ Outer.Inner.prototype.j = 2
|
|||
>j : any
|
||||
>2 : 2
|
||||
|
||||
/** @type {string} */
|
||||
Outer.Inner.prototype.k;
|
||||
>Outer.Inner.prototype.k : any
|
||||
>Outer.Inner.prototype : any
|
||||
>Outer.Inner : () => void
|
||||
>Outer : { [x: string]: any; Inner: () => void; }
|
||||
>Inner : () => void
|
||||
>prototype : any
|
||||
>k : any
|
||||
|
||||
var inner = new Outer.Inner()
|
||||
>inner : { [x: string]: any; m(): void; i: number; }
|
||||
>new Outer.Inner() : { [x: string]: any; m(): void; i: number; }
|
||||
>inner : { j: number; k: string; } & { [x: string]: any; m(): void; i: number; }
|
||||
>new Outer.Inner() : { j: number; k: string; } & { [x: string]: any; m(): void; i: number; }
|
||||
>Outer.Inner : () => void
|
||||
>Outer : { [x: string]: any; Inner: () => void; }
|
||||
>Inner : () => void
|
||||
|
@ -48,16 +58,21 @@ var inner = new Outer.Inner()
|
|||
inner.m()
|
||||
>inner.m() : void
|
||||
>inner.m : () => void
|
||||
>inner : { [x: string]: any; m(): void; i: number; }
|
||||
>inner : { j: number; k: string; } & { [x: string]: any; m(): void; i: number; }
|
||||
>m : () => void
|
||||
|
||||
inner.i
|
||||
>inner.i : number
|
||||
>inner : { [x: string]: any; m(): void; i: number; }
|
||||
>inner : { j: number; k: string; } & { [x: string]: any; m(): void; i: number; }
|
||||
>i : number
|
||||
|
||||
inner.j
|
||||
>inner.j : any
|
||||
>inner : { [x: string]: any; m(): void; i: number; }
|
||||
>j : any
|
||||
>inner.j : number
|
||||
>inner : { j: number; k: string; } & { [x: string]: any; m(): void; i: number; }
|
||||
>j : number
|
||||
|
||||
inner.k
|
||||
>inner.k : string
|
||||
>inner : { j: number; k: string; } & { [x: string]: any; m(): void; i: number; }
|
||||
>k : string
|
||||
|
||||
|
|
|
@ -9,9 +9,12 @@ Outer.Inner.prototype = {
|
|||
m() { },
|
||||
i: 1
|
||||
}
|
||||
// NOTE: incremental assignments don't work (but don't need to for chrome at least)
|
||||
// incremental assignments still work
|
||||
Outer.Inner.prototype.j = 2
|
||||
/** @type {string} */
|
||||
Outer.Inner.prototype.k;
|
||||
var inner = new Outer.Inner()
|
||||
inner.m()
|
||||
inner.i
|
||||
inner.j
|
||||
inner.k
|
||||
|
|
Loading…
Reference in a new issue