22/* @internal */
33namespace ts {
44 // The global Map object. This may not be available, so we must test for it.
5- declare const Map : NumberMapConstructor & StringMapConstructor | undefined ;
5+ declare const Map : { new < K , V > ( pairs ?: [ K , V ] [ ] ) : Map < K , V > } | undefined ;
66 const usingNativeMaps = typeof Map !== "undefined" ;
77 // tslint:disable-next-line:no-in-operator
88 const fullyFeaturedMaps = usingNativeMaps && "keys" in Map . prototype && "values" in Map . prototype && "entries" in Map . prototype ;
@@ -19,99 +19,63 @@ namespace ts {
1919 next ( ) : { value : T , done : false } | { value : never , done : true } ;
2020 }
2121
22- export interface NumberMapConstructor {
23- /**
24- * Creates a new Map with number keys.
25- * If `pairs` is provided, each [key, value] pair will be added to the map.
26- */
27- new < K extends number , V > ( pairs ?: [ K , V ] [ ] ) : Map < K , V > ;
28- }
29-
3022 /**
31- * In runtimes without Maps, this is implemented using a sparse array.
32- * This is generic over the key type because it is usually an enum.
23+ * Provides Map-like functionality for ES5 runtimes.
24+ * This is intentionally *not* a full Map shim, and doesn't provide iterators (which aren't available for IE Maps anyway).
25+ * We can only efficiently support strings and number keys, and iteration will always yield stringified keys.
3326 */
34- export const NumberMap : NumberMapConstructor = usingNativeMaps ? Map : class ShimNumberMap < K extends number , V > implements Map < K , V > {
35- private data : { [ key : number ] : V } = [ ] ;
27+ class ShimMap < K extends string | number , V > implements Map < K , V > {
28+ private data = createDictionaryModeObject < V > ( ) ;
29+
30+ /*
31+ So long as `K extends string | number`, we can cast `key as string` and insert it into the map.
32+ However, `forEach` will iterate over strings because values are stringified before being put in the map.
33+ */
3634
3735 constructor ( pairs ?: [ K , V ] [ ] ) {
3836 if ( pairs ) {
3937 for ( const [ key , value ] of pairs ) {
40- this . data [ key as number ] = value ;
38+ this . data [ key as string ] = value ;
4139 }
4240 }
4341 }
4442
45- clear ( ) {
46- this . data = [ ] ;
43+ clear ( ) : void {
44+ this . data = createDictionaryModeObject < V > ( ) ;
4745 }
4846
49- delete ( key : K ) {
50- delete this . data [ key as number ] ;
47+ delete ( key : K ) : void {
48+ delete this . data [ key as string ] ;
5149 }
5250
53- get ( key : K ) {
54- return this . data [ key as number ] ;
51+ get ( key : K ) : V {
52+ return this . data [ key as string ] ;
5553 }
5654
57- has ( key : K ) {
55+ has ( key : K ) : boolean {
5856 // tslint:disable-next-line:no-in-operator
59- return ( key as number ) in this . data ;
57+ return ( key as string ) in this . data ;
6058 }
6159
62- set ( key : K , value : V ) {
63- this . data [ key as number ] = value ;
60+ set ( key : K , value : V ) : void {
61+ this . data [ key as string ] = value ;
6462 }
6563
66- forEach ( action : ( value : V , key : K ) => void ) {
64+ forEach ( action : ( value : V , key : string ) => void ) : void {
6765 for ( const key in this . data ) {
68- action ( this . data [ key ] , key as any as K ) ;
66+ action ( this . data [ key ] , key ) ;
6967 }
7068 }
71- } ;
72-
73- export interface StringMapConstructor {
74- new < T > ( pairs ?: [ string , T ] [ ] ) : Map < string , T > ;
7569 }
76- /** In runtimes without Maps, this is implemented using an object. */
77- export const StringMap : StringMapConstructor = usingNativeMaps ? Map : class ShimStringMap < T > implements Map < string , T > {
78- private data = createDictionaryModeObject < T > ( ) ;
79-
80- constructor ( pairs ?: [ string , T ] [ ] ) {
81- if ( pairs ) {
82- for ( const [ key , value ] of pairs ) {
83- this . data [ key ] = value ;
84- }
85- }
86- }
87-
88- clear ( ) {
89- this . data = createDictionaryModeObject < T > ( ) ;
90- }
91-
92- delete ( key : string ) {
93- delete this . data [ key ] ;
94- }
95-
96- get ( key : string ) {
97- return this . data [ key ] ;
98- }
9970
100- has ( key : string ) {
101- // tslint:disable-next-line:no-in-operator
102- return key in this . data ;
103- }
104-
105- set ( key : string , value : T ) {
106- this . data [ key ] = value ;
107- }
71+ /**
72+ * In runtimes without Maps, this is implemented using an object.
73+ * `pairs` is an optional list of entries to add to the new map.
74+ */
75+ export const StringMap : { new < T > ( pairs ?: [ string , T ] [ ] ) : Map < string , T > ; } = usingNativeMaps ? Map : ShimMap ;
10876
109- forEach ( f : ( value : T , key : string ) => void ) {
110- for ( const key in this . data ) {
111- f ( this . data [ key ] , key ) ;
112- }
113- }
114- } ;
77+ /** This is generic over the key type because it is usually an enum. */
78+ export const NumberMap : { new < K extends number , V > ( pairs ?: [ K , V ] [ ] ) : Map < K , V > } = usingNativeMaps ? Map : ShimMap ;
11579
11680 const createObject = Object . create ;
11781 function createDictionaryModeObject < T > ( ) : MapLike < T > {
@@ -126,9 +90,12 @@ namespace ts {
12690 return map ;
12791 }
12892
129- /** Iterates over entries in the map, returning the first output of `getResult` that is not `undefined`. */
130- export const findInMap : < K , V , U > ( map : Map < K , V > , getResult : ( value : V , key : K ) => U | undefined ) => U | undefined = fullyFeaturedMaps
131- ? < K , V , U > ( map : ES6Map < K , V > , f : ( value : V , key : K ) => U | undefined ) => {
93+ /**
94+ * Iterates over entries in the map, returning the first output of `getResult` that is not `undefined`.
95+ * Only works for strings because shims iterate with `for-in`.
96+ */
97+ export const findInMap : < V , U > ( map : Map < string , V > , getResult : ( value : V , key : string ) => U | undefined ) => U | undefined = fullyFeaturedMaps
98+ ? < V , U > ( map : ES6Map < string , V > , f : ( value : V , key : string ) => U | undefined ) => {
13299 const iter = map . entries ( ) ;
133100 while ( true ) {
134101 const { value : pair , done } = iter . next ( ) ;
@@ -142,7 +109,7 @@ namespace ts {
142109 }
143110 }
144111 }
145- : < K , V , U > ( map : Map < K , V > , f : ( value : V , key : K ) => U | undefined ) => {
112+ : < V , U > ( map : Map < string , V > , f : ( value : V , key : string ) => U | undefined ) => {
146113 let result : U | undefined ;
147114 map . forEach ( ( value , key ) => {
148115 if ( result === undefined )
@@ -151,22 +118,28 @@ namespace ts {
151118 return result ;
152119 } ;
153120
154- /** Whether `predicate` is true for at least one entry in the map. */
155- export const someInMap : < K , V > ( map : Map < K , V > , predicate : ( value : V , key : K ) => boolean ) => boolean = fullyFeaturedMaps
156- ? < K , V > ( map : ES6Map < K , V > , predicate : ( value : V , key : K ) => boolean ) =>
121+ /**
122+ * Whether `predicate` is true for at least one entry in the map.
123+ * Only works for strings because shims iterate with `for-in`.
124+ */
125+ export const someInMap : < V > ( map : Map < string , V > , predicate : ( value : V , key : string ) => boolean ) => boolean = fullyFeaturedMaps
126+ ? < V > ( map : ES6Map < string , V > , predicate : ( value : V , key : string ) => boolean ) =>
157127 someInIterator ( map . entries ( ) , ( [ key , value ] ) => predicate ( value , key ) )
158- : < K , V > ( map : Map < K , V > , predicate : ( value : V , key : K ) => boolean ) => {
128+ : < V > ( map : Map < string , V > , predicate : ( value : V , key : string ) => boolean ) => {
159129 let found = false ;
160130 map . forEach ( ( value , key ) => {
161131 found = found || predicate ( value , key ) ;
162132 } ) ;
163133 return found ;
164134 } ;
165135
166- /** Whether `predicate` is true for at least one key in the map. */
167- export const someKeyInMap : < K > ( map : Map < K , any > , predicate : ( key : K ) => boolean ) => boolean = fullyFeaturedMaps
168- ? < K > ( map : ES6Map < K , any > , predicate : ( key : K ) => boolean ) => someInIterator ( map . keys ( ) , predicate )
169- : < K > ( map : Map < K , any > , predicate : ( key : K ) => boolean ) =>
136+ /**
137+ * Whether `predicate` is true for at least one key in the map.
138+ * Only works for strings because shims iterate with `for-in`.
139+ */
140+ export const someKeyInMap : ( map : Map < string , any > , predicate : ( key : string ) => boolean ) => boolean = fullyFeaturedMaps
141+ ? ( map : ES6Map < string , any > , predicate : ( key : string ) => boolean ) => someInIterator ( map . keys ( ) , predicate )
142+ : ( map : Map < string , any > , predicate : ( key : string ) => boolean ) =>
170143 someInMap ( map , ( _value , key ) => predicate ( key ) ) ;
171144
172145 /** Whether `predicate` is true for at least one value in the map. */
@@ -190,19 +163,20 @@ namespace ts {
190163 /**
191164 * Equivalent to the ES6 code:
192165 * `for (const key of map.keys()) action(key);`
166+ * Only works for strings because shims iterate with `for-in`.
193167 */
194- export const forEachKeyInMap : < K > ( map : Map < K , any > , action : ( key : K ) => void ) => void = fullyFeaturedMaps
195- ? < K > ( map : ES6Map < K , any > , f : ( key : K ) => void ) => {
196- const iter : Iterator < K > = map . keys ( ) ;
168+ export const forEachKeyInMap : ( map : Map < string , any > , action : ( key : string ) => void ) => void = fullyFeaturedMaps
169+ ? ( map : ES6Map < string , any > , action : ( key : string ) => void ) => {
170+ const iter : Iterator < string > = map . keys ( ) ;
197171 while ( true ) {
198172 const { value : key , done } = iter . next ( ) ;
199173 if ( done ) {
200174 return ;
201175 }
202- f ( key ) ;
176+ action ( key ) ;
203177 }
204178 }
205- : < K > ( map : Map < K , any > , action : ( key : K ) => void ) => {
179+ : ( map : Map < string , any > , action : ( key : string ) => void ) => {
206180 map . forEach ( ( _value , key ) => action ( key ) ) ;
207181 } ;
208182
@@ -310,14 +284,17 @@ namespace ts {
310284 * @param target A map to which properties should be copied.
311285 */
312286 export function copyMapEntriesFromTo < K , V > ( source : Map < K , V > , target : Map < K , V > ) : void {
313- source . forEach ( ( value , key ) => {
287+ source . forEach ( ( value : V , key : K ) => {
314288 target . set ( key , value ) ;
315289 } ) ;
316290 }
317291
318- /** Equivalent to `Array.from(map.keys())`. */
319- export function keysOfMap < K > ( map : Map < K , any > ) : K [ ] {
320- const keys : K [ ] = [ ] ;
292+ /**
293+ * Equivalent to `Array.from(map.keys())`.
294+ * Only works for strings because shims iterate with `for-in`.
295+ */
296+ export function keysOfMap ( map : Map < string , any > ) : string [ ] {
297+ const keys : string [ ] = [ ] ;
321298 forEachKeyInMap ( map , key => { keys . push ( key ) ; } ) ;
322299 return keys ;
323300 }
@@ -442,7 +419,7 @@ namespace ts {
442419 }
443420
444421 /** True if the maps have the same keys and values. */
445- export function mapsAreEqual < K , V > ( left : Map < K , V > , right : Map < K , V > , valuesAreEqual ?: ( left : V , right : V ) => boolean ) : boolean {
422+ export function mapsAreEqual < V > ( left : Map < string , V > , right : Map < string , V > , valuesAreEqual ?: ( left : V , right : V ) => boolean ) : boolean {
446423 if ( left === right ) return true ;
447424 if ( ! left || ! right ) return false ;
448425 const someInLeftHasNoMatch = someInMap ( left , ( leftValue , leftKey ) => {
0 commit comments