From 665c2ecf914c88fd4ee0b1a1e176b6e45fbec5ac Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 30 Nov 2017 10:27:38 -0800 Subject: [PATCH] Add SymbolFlag for containers of JS special decls And update some doc comments --- src/compiler/binder.ts | 6 ++++-- src/compiler/checker.ts | 16 ++++++++-------- src/compiler/types.ts | 1 + 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index bbfbef52ff..8145246adb 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2355,7 +2355,7 @@ namespace ts { } function bindPrototypePropertyAssignment(node: BinaryExpression) { - // We saw a node of the form 'x.prototype.y = z'. Declare a 'member' y on x if x was a function. + // We saw a node of the form 'x.prototype.y = z'. Declare a 'member' y on x if x is a function or class, or not declared. // Look up the function in the local scope, since prototype assignments should // follow the function declaration @@ -2372,7 +2372,7 @@ namespace ts { } /** - * For nodes like `x.y = z`, declare a member 'y' on 'x' if x was a function. + * For nodes like `x.y = z`, declare a member 'y' on 'x' if x is a function or class, or not declared. * Also works for expression statements preceded by JSDoc, like / ** @type number * / x.y; */ function bindStaticPropertyAssignment(node: BinaryExpression | PropertyAccessExpression) { @@ -2427,9 +2427,11 @@ namespace ts { const identifier = propertyAccess.expression as Identifier; if (targetSymbol) { addDeclarationToSymbol(symbol, identifier, SymbolFlags.Module); + symbol.flags |= SymbolFlags.JSContainer; } else { targetSymbol = declareSymbol(container.locals, /*parent*/ undefined, identifier, SymbolFlags.Module, SymbolFlags.ValueModuleExcludes); + targetSymbol.flags |= SymbolFlags.JSContainer; } } if (!targetSymbol || !(targetSymbol.flags & (SymbolFlags.Function | SymbolFlags.Class | SymbolFlags.NamespaceModule))) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ab2e2fbe2..f132854a03 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -633,8 +633,7 @@ namespace ts { function mergeSymbol(target: Symbol, source: Symbol) { if (!(target.flags & getExcludedSymbolFlags(source.flags)) || - source.valueDeclaration && source.valueDeclaration.kind === SyntaxKind.Identifier || - target.valueDeclaration && target.valueDeclaration.kind === SyntaxKind.Identifier) { + source.flags & SymbolFlags.JSContainer || target.flags & SymbolFlags.JSContainer) { // Javascript static-property-assignment declarations always merge, even though they are also values if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) { // reset flag when merging instantiated module into value module that has only const enums @@ -4480,7 +4479,9 @@ namespace ts { if (!jsDocType) { jsDocType = declarationType; } - else if (jsDocType !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(jsDocType, declarationType)) { + else if (jsDocType !== unknownType && declarationType !== unknownType && + !isTypeIdenticalTo(jsDocType, declarationType) && + !(symbol.flags & SymbolFlags.JSContainer)) { errorNextVariableOrPropertyDeclarationMustHaveSameType(symbol.valueDeclaration, jsDocType, declaration, declarationType); } } @@ -21435,7 +21436,10 @@ namespace ts { // Node is a secondary declaration, check that type is identical to primary declaration and check that // initializer is consistent with type associated with the node const declarationType = convertAutoToAny(getWidenedTypeForVariableLikeDeclaration(node)); - if (type !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(type, declarationType)) { + + if (type !== unknownType && declarationType !== unknownType && + !isTypeIdenticalTo(type, declarationType) && + !(symbol.flags & SymbolFlags.JSContainer)) { errorNextVariableOrPropertyDeclarationMustHaveSameType(symbol.valueDeclaration, type, node, declarationType); } if (node.initializer) { @@ -21461,10 +21465,6 @@ namespace ts { } function errorNextVariableOrPropertyDeclarationMustHaveSameType(firstDeclaration: Declaration, firstType: Type, nextDeclaration: Declaration, nextType: Type): void { - if (isIdentifier(firstDeclaration) || isIdentifier(nextDeclaration)) { - // js static assignment declarations don't have a type, so don't have to be consistent - return; - } const firstSourceFile = getSourceFileOfNode(firstDeclaration); const firstSpan = getErrorSpanForNode(firstSourceFile, getNameOfDeclaration(firstDeclaration) || firstDeclaration); const firstLocation = getLineAndCharacterOfPosition(firstSourceFile, firstSpan.start); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 68029b2f5e..380e7c7cc7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3059,6 +3059,7 @@ namespace ts { ExportStar = 1 << 23, // Export * declaration Optional = 1 << 24, // Optional property Transient = 1 << 25, // Transient symbol (created during type check) + JSContainer = 1 << 26, // Contains Javascript special declarations /* @internal */ All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral