Fixes for type parameter name resolution in JS (#26830)

* check for expando initializers in resolveEntityName

when resolving type parameters in a prototype property assignment
declaration. For example, this already works:

```js
/** @template T */
function f(x) { this.x = x }
/** @returns {T} */
f.protototype.m = function () { return this.x }
```

This now works too:

```js
/** @template T */
var f = function (x) { this.x = x }
/** @returns {T} */
f.prototype.m = function () { return this.x }
```

Fixes #26826

* Lookup type parameters on prototype-assignment methods

In the same way that they're looked up on prototype-property methods.

That is, this previously worked:

```js
/** @template T */
function f() { }
/** @param {T} p */
f.prototype.m = function () { }
```

And this now works too:

```js
/** @template T */
function f() { }
f.prototype = {
  /** @param {T} p */
  m() { }
}
```

Note that the baselines still have errors; I'll file a followup bug for
them.

* Look up types on property assignments too
This commit is contained in:
Nathan Shively-Sanders
2018-09-04 14:47:18 -07:00
committed by GitHub
parent 0ac3a0a937
commit 64ac5a53f4
8 changed files with 749 additions and 2 deletions

View File

@@ -2140,7 +2140,7 @@ namespace ts {
}
}
function getJSSpecialAssignmentLocation(node: TypeReferenceNode): Declaration | undefined {
function getJSSpecialAssignmentLocation(node: TypeReferenceNode): Node | undefined {
const typeAlias = findAncestor(node, node => !(isJSDocNode(node) || node.flags & NodeFlags.JSDoc) ? "quit" : isJSDocTypeAlias(node));
if (typeAlias) {
return;
@@ -2149,8 +2149,20 @@ namespace ts {
if (isExpressionStatement(host) &&
isBinaryExpression(host.expression) &&
getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
// X.prototype.m = /** @param {K} p */ function () { } <-- look for K on X's declaration
const symbol = getSymbolOfNode(host.expression.left);
return symbol && symbol.parent!.valueDeclaration;
if (symbol) {
return getDeclarationOfJSPrototypeContainer(symbol);
}
}
if ((isObjectLiteralMethod(host) || isPropertyAssignment(host)) &&
isBinaryExpression(host.parent.parent) &&
getSpecialPropertyAssignmentKind(host.parent.parent) === SpecialPropertyAssignmentKind.Prototype) {
// X.prototype = { /** @param {K} p */m() { } } <-- look for K on X's declaration
const symbol = getSymbolOfNode(host.parent.parent.left);
if (symbol) {
return getDeclarationOfJSPrototypeContainer(symbol);
}
}
const sig = getHostSignatureFromJSDocHost(host);
if (sig) {
@@ -2159,6 +2171,14 @@ namespace ts {
}
}
function getDeclarationOfJSPrototypeContainer(symbol: Symbol) {
const decl = symbol.parent!.valueDeclaration;
const initializer = isAssignmentDeclaration(decl) ? getAssignedJavascriptInitializer(decl) :
hasOnlyExpressionInitializer(decl) ? getDeclaredJavascriptInitializer(decl) :
undefined;
return initializer || decl;
}
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol | undefined {
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0);
}

View File

@@ -0,0 +1,113 @@
=== tests/cases/conformance/jsdoc/a.js ===
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
>Multimap : Symbol(Multimap, Decl(a.js, 0, 0))
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map : Symbol(Multimap._map, Decl(a.js, 6, 21))
>this : Symbol(Multimap, Decl(a.js, 0, 0))
>_map : Symbol(Multimap._map, Decl(a.js, 6, 21))
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Multimap.prototype.get = function (key) {
>Multimap.prototype : Symbol(Multimap.get, Decl(a.js, 9, 2))
>Multimap : Symbol(Multimap, Decl(a.js, 0, 0))
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
>get : Symbol(Multimap.get, Decl(a.js, 9, 2))
>key : Symbol(key, Decl(a.js, 15, 35))
return this._map[key + ''];
>this._map : Symbol(Multimap._map, Decl(a.js, 6, 21))
>this : Symbol(Multimap, Decl(a.js, 0, 0))
>_map : Symbol(Multimap._map, Decl(a.js, 6, 21))
>key : Symbol(key, Decl(a.js, 15, 35))
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
>Multimap2 : Symbol(Multimap2, Decl(a.js, 25, 3))
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map : Symbol(Multimap2._map, Decl(a.js, 25, 28))
>this : Symbol(Multimap2, Decl(a.js, 25, 15))
>_map : Symbol(Multimap2._map, Decl(a.js, 25, 28))
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Multimap2.prototype.get = function (key) {
>Multimap2.prototype : Symbol(Multimap2.get, Decl(a.js, 28, 2))
>Multimap2 : Symbol(Multimap2, Decl(a.js, 25, 3))
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
>get : Symbol(Multimap2.get, Decl(a.js, 28, 2))
>key : Symbol(key, Decl(a.js, 34, 36))
return this._map[key + ''];
>this._map : Symbol(Multimap2._map, Decl(a.js, 25, 28))
>this : Symbol(Multimap2, Decl(a.js, 25, 15))
>_map : Symbol(Multimap2._map, Decl(a.js, 25, 28))
>key : Symbol(key, Decl(a.js, 34, 36))
}
var Ns = {};
>Ns : Symbol(Ns, Decl(a.js, 38, 3), Decl(a.js, 38, 12))
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
>Ns.Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 38, 12))
>Ns : Symbol(Ns, Decl(a.js, 38, 3), Decl(a.js, 38, 12))
>Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 38, 12))
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map : Symbol(Multimap3._map, Decl(a.js, 45, 27))
>this : Symbol(Multimap3, Decl(a.js, 45, 14))
>_map : Symbol(Multimap3._map, Decl(a.js, 45, 27))
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Ns.Multimap3.prototype.get = function (key) {
>Ns.Multimap3.prototype : Symbol(Ns.Multimap3.get, Decl(a.js, 48, 2))
>Ns.Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 38, 12))
>Ns : Symbol(Ns, Decl(a.js, 38, 3), Decl(a.js, 38, 12))
>Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 38, 12))
>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --))
>get : Symbol(Ns.Multimap3.get, Decl(a.js, 48, 2))
>key : Symbol(key, Decl(a.js, 54, 39))
return this._map[key + ''];
>this._map : Symbol(Multimap3._map, Decl(a.js, 45, 27))
>this : Symbol(Multimap3, Decl(a.js, 45, 14))
>_map : Symbol(Multimap3._map, Decl(a.js, 45, 27))
>key : Symbol(key, Decl(a.js, 54, 39))
}

View File

@@ -0,0 +1,141 @@
=== tests/cases/conformance/jsdoc/a.js ===
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
>Multimap : typeof Multimap
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map = {} : {}
>this._map : { [x: string]: V; }
>this : Multimap
>_map : { [x: string]: V; }
>{} : {}
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Multimap.prototype.get = function (key) {
>Multimap.prototype.get = function (key) { return this._map[key + ''];} : (key: K) => V
>Multimap.prototype.get : any
>Multimap.prototype : any
>Multimap : typeof Multimap
>prototype : any
>get : any
>function (key) { return this._map[key + ''];} : (key: K) => V
>key : K
return this._map[key + ''];
>this._map[key + ''] : V
>this._map : { [x: string]: V; }
>this : Multimap
>_map : { [x: string]: V; }
>key + '' : string
>key : K
>'' : ""
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
>Multimap2 : typeof Multimap2
>function() { /** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */ this._map = {};} : typeof Multimap2
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map = {} : {}
>this._map : { [x: string]: V; }
>this : Multimap2
>_map : { [x: string]: V; }
>{} : {}
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Multimap2.prototype.get = function (key) {
>Multimap2.prototype.get = function (key) { return this._map[key + ''];} : (key: K) => V
>Multimap2.prototype.get : any
>Multimap2.prototype : any
>Multimap2 : typeof Multimap2
>prototype : any
>get : any
>function (key) { return this._map[key + ''];} : (key: K) => V
>key : K
return this._map[key + ''];
>this._map[key + ''] : V
>this._map : { [x: string]: V; }
>this : Multimap2
>_map : { [x: string]: V; }
>key + '' : string
>key : K
>'' : ""
}
var Ns = {};
>Ns : typeof Ns
>{} : {}
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
>Ns.Multimap3 = function() { /** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */ this._map = {};} : typeof Multimap3
>Ns.Multimap3 : typeof Multimap3
>Ns : typeof Ns
>Multimap3 : typeof Multimap3
>function() { /** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */ this._map = {};} : typeof Multimap3
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map = {} : {}
>this._map : { [x: string]: V; }
>this : Multimap3
>_map : { [x: string]: V; }
>{} : {}
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Ns.Multimap3.prototype.get = function (key) {
>Ns.Multimap3.prototype.get = function (key) { return this._map[key + ''];} : (key: K) => V
>Ns.Multimap3.prototype.get : any
>Ns.Multimap3.prototype : any
>Ns.Multimap3 : typeof Multimap3
>Ns : typeof Ns
>Multimap3 : typeof Multimap3
>prototype : any
>get : any
>function (key) { return this._map[key + ''];} : (key: K) => V
>key : K
return this._map[key + ''];
>this._map[key + ''] : V
>this._map : { [x: string]: V; }
>this : Multimap3
>_map : { [x: string]: V; }
>key + '' : string
>key : K
>'' : ""
}

View File

@@ -0,0 +1,77 @@
tests/cases/conformance/jsdoc/a.js(18,21): error TS2339: Property '_map' does not exist on type '{ get(key: K): V; }'.
tests/cases/conformance/jsdoc/a.js(39,21): error TS2339: Property '_map' does not exist on type '{ get: (key: K) => V; }'.
tests/cases/conformance/jsdoc/a.js(61,21): error TS2339: Property '_map' does not exist on type '{ get(key: K): V; }'.
==== tests/cases/conformance/jsdoc/a.js (3 errors) ====
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
Multimap.prototype = {
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
return this._map[key + ''];
~~~~
!!! error TS2339: Property '_map' does not exist on type '{ get(key: K): V; }'.
}
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
Multimap2.prototype = {
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get: function(key) {
return this._map[key + ''];
~~~~
!!! error TS2339: Property '_map' does not exist on type '{ get: (key: K) => V; }'.
}
}
var Ns = {};
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
Ns.Multimap3.prototype = {
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
return this._map[key + ''];
~~~~
!!! error TS2339: Property '_map' does not exist on type '{ get(key: K): V; }'.
}
}

View File

@@ -0,0 +1,114 @@
=== tests/cases/conformance/jsdoc/a.js ===
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
>Multimap : Symbol(Multimap, Decl(a.js, 0, 0), Decl(a.js, 9, 2))
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map : Symbol(Multimap._map, Decl(a.js, 6, 21))
>_map : Symbol(Multimap._map, Decl(a.js, 6, 21))
};
Multimap.prototype = {
>Multimap.prototype : Symbol(Multimap.prototype, Decl(a.js, 9, 2))
>Multimap : Symbol(Multimap, Decl(a.js, 0, 0), Decl(a.js, 9, 2))
>prototype : Symbol(Multimap.prototype, Decl(a.js, 9, 2))
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
>get : Symbol(get, Decl(a.js, 11, 22))
>key : Symbol(key, Decl(a.js, 16, 8))
return this._map[key + ''];
>this : Symbol(__object, Decl(a.js, 11, 20))
>key : Symbol(key, Decl(a.js, 16, 8))
}
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
>Multimap2 : Symbol(Multimap2, Decl(a.js, 27, 3), Decl(a.js, 30, 2))
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map : Symbol(Multimap2._map, Decl(a.js, 27, 28))
>_map : Symbol(Multimap2._map, Decl(a.js, 27, 28))
};
Multimap2.prototype = {
>Multimap2.prototype : Symbol(Multimap2.prototype, Decl(a.js, 30, 2))
>Multimap2 : Symbol(Multimap2, Decl(a.js, 27, 3), Decl(a.js, 30, 2))
>prototype : Symbol(Multimap2.prototype, Decl(a.js, 30, 2))
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get: function(key) {
>get : Symbol(get, Decl(a.js, 32, 23))
>key : Symbol(key, Decl(a.js, 37, 18))
return this._map[key + ''];
>this : Symbol(__object, Decl(a.js, 32, 21))
>key : Symbol(key, Decl(a.js, 37, 18))
}
}
var Ns = {};
>Ns : Symbol(Ns, Decl(a.js, 42, 3), Decl(a.js, 42, 12), Decl(a.js, 52, 2))
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
>Ns.Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 42, 12), Decl(a.js, 54, 3))
>Ns : Symbol(Ns, Decl(a.js, 42, 3), Decl(a.js, 42, 12), Decl(a.js, 52, 2))
>Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 42, 12), Decl(a.js, 54, 3))
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map : Symbol(Multimap3._map, Decl(a.js, 49, 27))
>_map : Symbol(Multimap3._map, Decl(a.js, 49, 27))
};
Ns.Multimap3.prototype = {
>Ns.Multimap3.prototype : Symbol(Ns.Multimap3.prototype, Decl(a.js, 52, 2))
>Ns.Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 42, 12), Decl(a.js, 54, 3))
>Ns : Symbol(Ns, Decl(a.js, 42, 3), Decl(a.js, 42, 12), Decl(a.js, 52, 2))
>Multimap3 : Symbol(Ns.Multimap3, Decl(a.js, 42, 12), Decl(a.js, 54, 3))
>prototype : Symbol(Ns.Multimap3.prototype, Decl(a.js, 52, 2))
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
>get : Symbol(get, Decl(a.js, 54, 26))
>key : Symbol(key, Decl(a.js, 59, 8))
return this._map[key + ''];
>this : Symbol(__object, Decl(a.js, 54, 24))
>key : Symbol(key, Decl(a.js, 59, 8))
}
}

View File

@@ -0,0 +1,149 @@
=== tests/cases/conformance/jsdoc/a.js ===
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
>Multimap : typeof Multimap
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map = {} : {}
>this._map : { [x: string]: V; }
>this : Multimap & { get(key: K): V; }
>_map : { [x: string]: V; }
>{} : {}
};
Multimap.prototype = {
>Multimap.prototype = { /** * @param {K} key the key ok * @returns {V} the value ok */ get(key) { return this._map[key + '']; }} : { get(key: K): V; }
>Multimap.prototype : { get(key: K): V; }
>Multimap : typeof Multimap
>prototype : { get(key: K): V; }
>{ /** * @param {K} key the key ok * @returns {V} the value ok */ get(key) { return this._map[key + '']; }} : { get(key: K): V; }
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
>get : (key: K) => V
>key : K
return this._map[key + ''];
>this._map[key + ''] : any
>this._map : any
>this : { get(key: K): V; }
>_map : any
>key + '' : string
>key : K
>'' : ""
}
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
>Multimap2 : typeof Multimap2
>function() { /** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */ this._map = {};} : typeof Multimap2
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map = {} : {}
>this._map : { [x: string]: V; }
>this : Multimap2 & { get: (key: K) => V; }
>_map : { [x: string]: V; }
>{} : {}
};
Multimap2.prototype = {
>Multimap2.prototype = { /** * @param {K} key the key ok * @returns {V} the value ok */ get: function(key) { return this._map[key + '']; }} : { get: (key: K) => V; }
>Multimap2.prototype : { get: (key: K) => V; }
>Multimap2 : typeof Multimap2
>prototype : { get: (key: K) => V; }
>{ /** * @param {K} key the key ok * @returns {V} the value ok */ get: function(key) { return this._map[key + '']; }} : { get: (key: K) => V; }
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get: function(key) {
>get : (key: K) => V
>function(key) { return this._map[key + '']; } : (key: K) => V
>key : K
return this._map[key + ''];
>this._map[key + ''] : any
>this._map : any
>this : { get: (key: K) => V; }
>_map : any
>key + '' : string
>key : K
>'' : ""
}
}
var Ns = {};
>Ns : typeof Ns
>{} : {}
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
>Ns.Multimap3 = function() { /** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */ this._map = {};} : typeof Multimap3
>Ns.Multimap3 : typeof Multimap3
>Ns : typeof Ns
>Multimap3 : typeof Multimap3
>function() { /** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */ this._map = {};} : typeof Multimap3
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
>this._map = {} : {}
>this._map : { [x: string]: V; }
>this : Multimap3 & { get(key: K): V; }
>_map : { [x: string]: V; }
>{} : {}
};
Ns.Multimap3.prototype = {
>Ns.Multimap3.prototype = { /** * @param {K} key the key ok * @returns {V} the value ok */ get(key) { return this._map[key + '']; }} : { get(key: K): V; }
>Ns.Multimap3.prototype : { get(key: K): V; }
>Ns.Multimap3 : typeof Multimap3
>Ns : typeof Ns
>Multimap3 : typeof Multimap3
>prototype : { get(key: K): V; }
>{ /** * @param {K} key the key ok * @returns {V} the value ok */ get(key) { return this._map[key + '']; }} : { get(key: K): V; }
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
>get : (key: K) => V
>key : K
return this._map[key + ''];
>this._map[key + ''] : any
>this._map : any
>this : { get(key: K): V; }
>_map : any
>key + '' : string
>key : K
>'' : ""
}
}

View File

@@ -0,0 +1,63 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @strict: true
// @Filename: a.js
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Multimap.prototype.get = function (key) {
return this._map[key + ''];
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Multimap2.prototype.get = function (key) {
return this._map[key + ''];
}
var Ns = {};
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
Ns.Multimap3.prototype.get = function (key) {
return this._map[key + ''];
}

View File

@@ -0,0 +1,70 @@
// @noEmit: true
// @allowJs: true
// @checkJs: true
// @strict: true
// @Filename: a.js
/**
* Should work for function declarations
* @constructor
* @template {string} K
* @template V
*/
function Multimap() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
Multimap.prototype = {
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
return this._map[key + ''];
}
}
/**
* Should work for initialisers too
* @constructor
* @template {string} K
* @template V
*/
var Multimap2 = function() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
Multimap2.prototype = {
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get: function(key) {
return this._map[key + ''];
}
}
var Ns = {};
/**
* Should work for expando-namespaced initialisers too
* @constructor
* @template {string} K
* @template V
*/
Ns.Multimap3 = function() {
/** @type {Object<string, V>} TODO: Remove the prototype from the fresh object */
this._map = {};
};
Ns.Multimap3.prototype = {
/**
* @param {K} key the key ok
* @returns {V} the value ok
*/
get(key) {
return this._map[key + ''];
}
}