mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-13 11:46:08 -05:00
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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user