Merge branch 'master' into referencesPrototypeSourceFile

This commit is contained in:
Sheetal Nandi 2019-08-22 14:57:38 -07:00
commit 6e091696fb
13 changed files with 363 additions and 99 deletions

View File

@ -2560,6 +2560,22 @@ namespace ts {
return initializer || decl;
}
/**
* Get the real symbol of a declaration with an expando initializer.
*
* Normally, declarations have an associated symbol, but when a declaration has an expando
* initializer, the expando's symbol is the one that has all the members merged into it.
*/
function getExpandoSymbol(symbol: Symbol): Symbol | undefined {
const decl = symbol.valueDeclaration;
if (!decl || !isInJSFile(decl) || symbol.flags & SymbolFlags.TypeAlias) {
return undefined;
}
const init = isVariableDeclaration(decl) ? getDeclaredExpandoInitializer(decl) : getAssignedExpandoInitializer(decl);
return init && getSymbolOfNode(init) || undefined;
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression, ignoreErrors?: boolean): Symbol | undefined {
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, ignoreErrors ? undefined : Diagnostics.Cannot_find_module_0);
}
@ -9195,10 +9211,13 @@ namespace ts {
if (symbol === unknownSymbol) {
return errorType;
}
symbol = getExpandoSymbol(symbol) || symbol;
const type = getTypeReferenceTypeWorker(node, symbol, typeArguments);
if (type) {
return type;
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
}
// Get type from reference to named type that cannot be generic (enum or type parameter)
@ -9209,62 +9228,34 @@ namespace ts {
errorType;
}
if (!(symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node))) {
return errorType;
if (symbol.flags & SymbolFlags.Value && isJSDocTypeReference(node)) {
const jsdocType = getTypeFromJSAlias(node, symbol);
if (jsdocType) {
return jsdocType;
}
else {
// Resolve the type reference as a Type for the purpose of reporting errors.
resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type);
return getTypeOfSymbol(symbol);
}
}
const jsdocType = getJSDocTypeReference(node, symbol, typeArguments);
if (jsdocType) {
return jsdocType;
}
// Resolve the type reference as a Type for the purpose of reporting errors.
resolveTypeReferenceName(getTypeReferenceName(node), SymbolFlags.Type);
return getTypeOfSymbol(symbol);
return errorType;
}
/**
* A jsdoc TypeReference may have resolved to a value (as opposed to a type). If
* the symbol is a constructor function, return the inferred class type; otherwise,
* the type of this reference is just the type of the value we resolved to.
* A JSdoc TypeReference may be to a value imported from commonjs.
* These should really be aliases, but this special-case code fakes alias resolution
* by producing a type from a value.
*/
function getJSDocTypeReference(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined {
// In the case of an assignment of a function expression (binary expressions, variable declarations, etc.), we will get the
// correct instance type for the symbol on the LHS by finding the type for RHS. For example if we want to get the type of the symbol `foo`:
// var foo = function() {}
// We will find the static type of the assigned anonymous function.
const staticType = getTypeOfSymbol(symbol);
const instanceType =
staticType.symbol &&
staticType.symbol !== symbol && // Make sure this is an assignment like expression by checking that symbol -> type -> symbol doesn't roundtrips.
getTypeReferenceTypeWorker(node, staticType.symbol, typeArguments); // Get the instance type of the RHS symbol.
if (instanceType) {
return getSymbolLinks(symbol).resolvedJSDocType = instanceType;
}
}
function getTypeReferenceTypeWorker(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[] | undefined): Type | undefined {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
if (symbol.valueDeclaration && symbol.valueDeclaration.parent && isBinaryExpression(symbol.valueDeclaration.parent)) {
const jsdocType = getJSDocTypeReference(node, symbol, typeArguments);
if (jsdocType) {
return jsdocType;
}
}
return getTypeFromClassOrInterfaceReference(node, symbol, typeArguments);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getTypeFromTypeAliasReference(node, symbol, typeArguments);
}
if (symbol.flags & SymbolFlags.Function &&
isJSDocTypeReference(node) &&
isJSConstructor(symbol.valueDeclaration)) {
const resolved = resolveStructuredTypeMembers(<ObjectType>getTypeOfSymbol(symbol));
if (resolved.callSignatures.length === 1) {
return getReturnTypeOfSignature(resolved.callSignatures[0]);
}
function getTypeFromJSAlias(node: NodeWithTypeArguments, symbol: Symbol): Type | undefined {
const valueType = getTypeOfSymbol(symbol);
const typeType =
valueType.symbol &&
valueType.symbol !== symbol && // Make sure this is a commonjs export by checking that symbol -> type -> symbol doesn't roundtrip.
getTypeReferenceType(node, valueType.symbol);
if (typeType) {
return getSymbolLinks(symbol).resolvedJSDocType = typeType;
}
}
@ -22879,24 +22870,29 @@ namespace ts {
// If the symbol of the node has members, treat it like a constructor.
const symbol = getSymbolOfNode(func);
return !!symbol && symbol.members !== undefined;
return !!symbol && hasEntries(symbol.members);
}
return false;
}
function mergeJSSymbols(target: Symbol, source: Symbol | undefined) {
if (source && (hasEntries(source.exports) || hasEntries(source.members))) {
target = cloneSymbol(target);
if (hasEntries(source.exports)) {
target.exports = target.exports || createSymbolTable();
mergeSymbolTable(target.exports, source.exports);
const links = getSymbolLinks(source);
if (!links.inferredClassSymbol || !links.inferredClassSymbol.has("" + getSymbolId(target))) {
const inferred = isTransientSymbol(target) ? target : cloneSymbol(target) as TransientSymbol;
inferred.exports = inferred.exports || createSymbolTable();
inferred.members = inferred.members || createSymbolTable();
inferred.flags |= source.flags & SymbolFlags.Class;
if (hasEntries(source.exports)) {
mergeSymbolTable(inferred.exports, source.exports);
}
if (hasEntries(source.members)) {
mergeSymbolTable(inferred.members, source.members);
}
(links.inferredClassSymbol || (links.inferredClassSymbol = createMap<TransientSymbol>())).set("" + getSymbolId(inferred), inferred);
return inferred;
}
if (hasEntries(source.members)) {
target.members = target.members || createSymbolTable();
mergeSymbolTable(target.members, source.members);
}
target.flags |= source.flags & SymbolFlags.Class;
return target as TransientSymbol;
return links.inferredClassSymbol.get("" + getSymbolId(target));
}
}

View File

@ -3772,30 +3772,31 @@ namespace ts {
/* @internal */
export interface SymbolLinks {
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
target?: Symbol; // Resolved (non-alias) target of an alias
type?: Type; // Type of value symbol
uniqueESSymbolType?: Type; // UniqueESSymbol type for a symbol
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic)
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic)
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
leftSpread?: Symbol; // Left source for synthetic spread property
rightSpread?: Symbol; // Right source for synthetic spread property
syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class.
resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol
exportsChecked?: boolean; // True if exports of external module have been checked
typeParametersChecked?: boolean; // True if type parameters of merged class and interface declarations have been checked.
immediateTarget?: Symbol; // Immediate target of an alias. May be another alias. Do not access directly, use `checker.getImmediateAliasedSymbol` instead.
target?: Symbol; // Resolved (non-alias) target of an alias
type?: Type; // Type of value symbol
uniqueESSymbolType?: Type; // UniqueESSymbol type for a symbol
declaredType?: Type; // Type of class, interface, enum, type alias, or type parameter
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
typeParameters?: TypeParameter[]; // Type parameters of type alias (undefined if non-generic)
outerTypeParameters?: TypeParameter[]; // Outer type parameters of anonymous object type
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic)
inferredClassSymbol?: Map<TransientSymbol>; // Symbol of an inferred ES5 constructor function
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
leftSpread?: Symbol; // Left source for synthetic spread property
rightSpread?: Symbol; // Right source for synthetic spread property
syntheticOrigin?: Symbol; // For a property on a mapped or spread type, points back to the original property
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module or combined early- and late-bound static members of a class.
resolvedMembers?: SymbolTable; // Combined early- and late-bound members of a symbol
exportsChecked?: boolean; // True if exports of external module have been checked
typeParametersChecked?: boolean; // True if type parameters of merged class and interface declarations have been checked.
isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration
bindingElement?: BindingElement; // Binding element associated with property symbol
exportsSomeValue?: boolean; // True if module exports some value (not just types)
enumKind?: EnumKind; // Enum declaration classification
bindingElement?: BindingElement; // Binding element associated with property symbol
exportsSomeValue?: boolean; // True if module exports some value (not just types)
enumKind?: EnumKind; // Enum declaration classification
originatingImport?: ImportDeclaration | ImportCall; // Import declaration which produced the symbol, present if the symbol is marked as uncallable but had call signatures in `resolveESModuleSymbol`
lateSymbol?: Symbol; // Late-bound symbol for a computed property
specifierCache?: Map<string>; // For symbols corresponding to external modules, a cache of incoming path -> module specifier name mappings

View File

@ -19,10 +19,10 @@ tests/cases/conformance/salsa/use.js(6,5): error TS2345: Argument of type '"not
declare var exports: any;
==== tests/cases/conformance/salsa/mod.js (0 errors) ====
/// <reference path='./types.d.ts'/>
var A = function() {
var A = function A() {
this.a = 1
}
var B = function() {
var B = function B() {
this.b = 2
}
exports.A = A

View File

@ -37,17 +37,19 @@ declare var exports: any;
=== tests/cases/conformance/salsa/mod.js ===
/// <reference path='./types.d.ts'/>
var A = function() {
var A = function A() {
>A : Symbol(A, Decl(mod.js, 1, 3), Decl(mod.js, 8, 13))
>A : Symbol(A, Decl(mod.js, 1, 7))
this.a = 1
>a : Symbol(A.a, Decl(mod.js, 1, 20))
>a : Symbol(A.a, Decl(mod.js, 1, 22))
}
var B = function() {
var B = function B() {
>B : Symbol(B, Decl(mod.js, 4, 3), Decl(mod.js, 9, 13))
>B : Symbol(B, Decl(mod.js, 4, 7))
this.b = 2
>b : Symbol(B.b, Decl(mod.js, 4, 20))
>b : Symbol(B.b, Decl(mod.js, 4, 22))
}
exports.A = A
>exports.A : Symbol(A, Decl(mod.js, 6, 1))

View File

@ -44,9 +44,10 @@ declare var exports: any;
=== tests/cases/conformance/salsa/mod.js ===
/// <reference path='./types.d.ts'/>
var A = function() {
var A = function A() {
>A : typeof A
>function A() { this.a = 1} : typeof A
>A : typeof A
>function() { this.a = 1} : typeof A
this.a = 1
>this.a = 1 : 1
@ -55,9 +56,10 @@ var A = function() {
>a : any
>1 : 1
}
var B = function() {
var B = function B() {
>B : typeof B
>function B() { this.b = 2} : typeof B
>B : typeof B
>function() { this.b = 2} : typeof B
this.b = 2
>this.b = 2 : 2

View File

@ -0,0 +1,31 @@
=== tests/cases/conformance/salsa/jsdocConstructorFunctionTypeReference.js ===
var Validator = function VFunc() {
>Validator : Symbol(Validator, Decl(jsdocConstructorFunctionTypeReference.js, 0, 3))
>VFunc : Symbol(VFunc, Decl(jsdocConstructorFunctionTypeReference.js, 0, 15))
this.flags = "gim"
>flags : Symbol(VFunc.flags, Decl(jsdocConstructorFunctionTypeReference.js, 0, 34))
};
Validator.prototype.num = 12
>Validator.prototype : Symbol(Validator.num, Decl(jsdocConstructorFunctionTypeReference.js, 2, 2))
>Validator : Symbol(Validator, Decl(jsdocConstructorFunctionTypeReference.js, 0, 3))
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
>num : Symbol(Validator.num, Decl(jsdocConstructorFunctionTypeReference.js, 2, 2))
/**
* @param {Validator} state
*/
var validateRegExpFlags = function(state) {
>validateRegExpFlags : Symbol(validateRegExpFlags, Decl(jsdocConstructorFunctionTypeReference.js, 9, 3))
>state : Symbol(state, Decl(jsdocConstructorFunctionTypeReference.js, 9, 35))
return state.flags
>state.flags : Symbol(VFunc.flags, Decl(jsdocConstructorFunctionTypeReference.js, 0, 34))
>state : Symbol(state, Decl(jsdocConstructorFunctionTypeReference.js, 9, 35))
>flags : Symbol(VFunc.flags, Decl(jsdocConstructorFunctionTypeReference.js, 0, 34))
};

View File

@ -0,0 +1,40 @@
=== tests/cases/conformance/salsa/jsdocConstructorFunctionTypeReference.js ===
var Validator = function VFunc() {
>Validator : typeof VFunc
>function VFunc() { this.flags = "gim"} : typeof VFunc
>VFunc : typeof VFunc
this.flags = "gim"
>this.flags = "gim" : "gim"
>this.flags : any
>this : any
>flags : any
>"gim" : "gim"
};
Validator.prototype.num = 12
>Validator.prototype.num = 12 : 12
>Validator.prototype.num : any
>Validator.prototype : any
>Validator : typeof VFunc
>prototype : any
>num : any
>12 : 12
/**
* @param {Validator} state
*/
var validateRegExpFlags = function(state) {
>validateRegExpFlags : (state: VFunc) => string
>function(state) { return state.flags} : (state: VFunc) => string
>state : VFunc
return state.flags
>state.flags : string
>state : VFunc
>flags : string
};

View File

@ -0,0 +1,31 @@
tests/cases/conformance/salsa/other.js(5,5): error TS2339: Property 'wat' does not exist on type 'One'.
tests/cases/conformance/salsa/other.js(10,5): error TS2339: Property 'wat' does not exist on type 'Two'.
==== tests/cases/conformance/salsa/prototypePropertyAssignmentMergeAcrossFiles2.js (0 errors) ====
var Ns = {}
Ns.One = function() {};
Ns.Two = function() {};
Ns.One.prototype = {
ok() {},
};
Ns.Two.prototype = {
}
==== tests/cases/conformance/salsa/other.js (2 errors) ====
/**
* @type {Ns.One}
*/
var one;
one.wat;
~~~
!!! error TS2339: Property 'wat' does not exist on type 'One'.
/**
* @type {Ns.Two}
*/
var two;
two.wat;
~~~
!!! error TS2339: Property 'wat' does not exist on type 'Two'.

View File

@ -0,0 +1,52 @@
=== tests/cases/conformance/salsa/prototypePropertyAssignmentMergeAcrossFiles2.js ===
var Ns = {}
>Ns : Symbol(Ns, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 3), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
Ns.One = function() {};
>Ns.One : Symbol(Ns.One, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 4, 3))
>Ns : Symbol(Ns, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 3), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
>One : Symbol(Ns.One, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 4, 3))
Ns.Two = function() {};
>Ns.Two : Symbol(Ns.Two, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 7, 3))
>Ns : Symbol(Ns, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 3), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
>Two : Symbol(Ns.Two, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 7, 3))
Ns.One.prototype = {
>Ns.One.prototype : Symbol(Ns.One.prototype, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23))
>Ns.One : Symbol(Ns.One, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 4, 3))
>Ns : Symbol(Ns, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 3), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
>One : Symbol(Ns.One, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 4, 3))
>prototype : Symbol(Ns.One.prototype, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23))
ok() {},
>ok : Symbol(ok, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 4, 20))
};
Ns.Two.prototype = {
>Ns.Two.prototype : Symbol(Ns.Two.prototype, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
>Ns.Two : Symbol(Ns.Two, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 7, 3))
>Ns : Symbol(Ns, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 3), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 0, 11), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 2, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
>Two : Symbol(Ns.Two, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 1, 23), Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 7, 3))
>prototype : Symbol(Ns.Two.prototype, Decl(prototypePropertyAssignmentMergeAcrossFiles2.js, 6, 2))
}
=== tests/cases/conformance/salsa/other.js ===
/**
* @type {Ns.One}
*/
var one;
>one : Symbol(one, Decl(other.js, 3, 3))
one.wat;
>one : Symbol(one, Decl(other.js, 3, 3))
/**
* @type {Ns.Two}
*/
var two;
>two : Symbol(two, Decl(other.js, 8, 3))
two.wat;
>two : Symbol(two, Decl(other.js, 8, 3))

View File

@ -0,0 +1,65 @@
=== tests/cases/conformance/salsa/prototypePropertyAssignmentMergeAcrossFiles2.js ===
var Ns = {}
>Ns : typeof Ns
>{} : {}
Ns.One = function() {};
>Ns.One = function() {} : typeof One
>Ns.One : typeof One
>Ns : typeof Ns
>One : typeof One
>function() {} : typeof One
Ns.Two = function() {};
>Ns.Two = function() {} : typeof Two
>Ns.Two : typeof Two
>Ns : typeof Ns
>Two : typeof Two
>function() {} : typeof Two
Ns.One.prototype = {
>Ns.One.prototype = { ok() {},} : { ok(): void; }
>Ns.One.prototype : { ok(): void; }
>Ns.One : typeof One
>Ns : typeof Ns
>One : typeof One
>prototype : { ok(): void; }
>{ ok() {},} : { ok(): void; }
ok() {},
>ok : () => void
};
Ns.Two.prototype = {
>Ns.Two.prototype = {} : {}
>Ns.Two.prototype : {}
>Ns.Two : typeof Two
>Ns : typeof Ns
>Two : typeof Two
>prototype : {}
>{} : {}
}
=== tests/cases/conformance/salsa/other.js ===
/**
* @type {Ns.One}
*/
var one;
>one : One
one.wat;
>one.wat : any
>one : One
>wat : any
/**
* @type {Ns.Two}
*/
var two;
>two : Two
two.wat;
>two.wat : any
>two : Two
>wat : any

View File

@ -7,10 +7,10 @@ declare function require(name: string): any;
declare var exports: any;
// @Filename: mod.js
/// <reference path='./types.d.ts'/>
var A = function() {
var A = function A() {
this.a = 1
}
var B = function() {
var B = function B() {
this.b = 2
}
exports.A = A

View File

@ -0,0 +1,19 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @noImplicitAny: true
// @Filename: jsdocConstructorFunctionTypeReference.js
var Validator = function VFunc() {
this.flags = "gim"
};
Validator.prototype.num = 12
/**
* @param {Validator} state
*/
var validateRegExpFlags = function(state) {
return state.flags
};

View File

@ -0,0 +1,25 @@
// @Filename: prototypePropertyAssignmentMergeAcrossFiles2.js
// @allowJs: true
// @checkJs: true
// @noEmit: true
var Ns = {}
Ns.One = function() {};
Ns.Two = function() {};
Ns.One.prototype = {
ok() {},
};
Ns.Two.prototype = {
}
// @Filename: other.js
/**
* @type {Ns.One}
*/
var one;
one.wat;
/**
* @type {Ns.Two}
*/
var two;
two.wat;