Skip to content

Commit 374ce8b

Browse files
committed
Add full implemention of Map and Set to shims
1 parent 01b3d41 commit 374ce8b

File tree

8 files changed

+895
-288
lines changed

8 files changed

+895
-288
lines changed

src/compiler/core.ts

Lines changed: 140 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,81 @@ namespace ts {
2525
}
2626

2727
/** ES6 Map interface, only read methods included. */
28-
export interface ReadonlyMap<T> {
29-
get(key: string): T | undefined;
30-
has(key: string): boolean;
31-
forEach(action: (value: T, key: string) => void): void;
28+
export interface ReadonlyESMap<K, V> {
29+
get(key: K): V | undefined;
30+
has(key: K): boolean;
31+
forEach(action: (value: V, key: K) => void): void;
3232
readonly size: number;
33-
keys(): Iterator<string>;
34-
values(): Iterator<T>;
35-
entries(): Iterator<[string, T]>;
33+
keys(): Iterator<K>;
34+
values(): Iterator<V>;
35+
entries(): Iterator<[K, V]>;
3636
}
3737

3838
/** ES6 Map interface. */
39-
export interface Map<T> extends ReadonlyMap<T> {
40-
set(key: string, value: T): this;
41-
delete(key: string): boolean;
39+
export interface ESMap<K, V> extends ReadonlyESMap<K, V> {
40+
set(key: K, value: V): this;
41+
delete(key: K): boolean;
4242
clear(): void;
4343
}
4444

45+
/** ES6 Map interface, only read methods included. */
46+
export interface ReadonlyMap<T> extends ReadonlyESMap<string, T> {
47+
}
48+
49+
/** ES6 Map interface. */
50+
export interface Map<T> extends ESMap<string, T>, ReadonlyMap<T> {
51+
}
52+
4553
/* @internal */
4654
export interface MapConstructor {
4755
// eslint-disable-next-line @typescript-eslint/prefer-function-type
48-
new <T>(): Map<T>;
56+
new <K, V>(): ESMap<K, V>;
57+
}
58+
59+
export interface ReadonlySet<T> {
60+
readonly size: number;
61+
has(value: T): boolean;
62+
forEach(action: (value: T, key: T) => void): void;
63+
keys(): Iterator<T>;
64+
values(): Iterator<T>;
65+
entries(): Iterator<[T, T]>;
66+
}
67+
68+
export interface Set<T> extends ReadonlySet<T> {
69+
add(value: T): this;
70+
delete(value: T): boolean;
71+
clear(): void;
72+
}
73+
74+
/* @internal */
75+
export interface SetConstructor {
76+
// eslint-disable-next-line @typescript-eslint/prefer-function-type
77+
new <T>(): Set<T>;
78+
}
79+
80+
export interface WeakMap<K extends object, V> {
81+
get(key: K): V | undefined;
82+
has(key: K): boolean;
83+
set(key: K, value: V): this;
84+
delete(key: K): boolean;
85+
}
86+
87+
/* @internal */
88+
export interface WeakMapConstructor {
89+
// eslint-disable-next-line @typescript-eslint/prefer-function-type
90+
new <K extends object, V>(): WeakMap<K, V>;
91+
}
92+
93+
export interface WeakSet<T extends object> {
94+
has(key: T): boolean;
95+
add(key: T): this;
96+
delete(key: T): boolean;
97+
}
98+
99+
/* @internal */
100+
export interface WeakSetConstructor {
101+
// eslint-disable-next-line @typescript-eslint/prefer-function-type
102+
new <T extends object>(): WeakSet<T>;
49103
}
50104

51105
/** ES6 Iterator type. */
@@ -75,8 +129,12 @@ namespace ts {
75129
/* @internal */
76130
namespace ts {
77131
// Natives
78-
// NOTE: This must be declared in a separate block from the one below so that we don't collide with the exported definition of `Map`.
79-
declare const Map: (new <T>() => Map<T>) | undefined;
132+
// NOTE: This must be declared in a separate block from the one below so that we don't collide with the exported definitions of `Map` and `Set`.
133+
134+
declare const Map: MapConstructor | undefined;
135+
declare const Set: SetConstructor | undefined;
136+
declare const WeakMap: WeakMapConstructor | undefined;
137+
declare const WeakSet: WeakSetConstructor | undefined;
80138

81139
/**
82140
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
@@ -86,28 +144,79 @@ namespace ts {
86144
// eslint-disable-next-line no-in-operator
87145
return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined;
88146
}
147+
148+
/**
149+
* Returns the native Set implementation if it is available and compatible (i.e. supports iteration).
150+
*/
151+
export function tryGetNativeSet(): SetConstructor | undefined {
152+
// Internet Explorer's Set doesn't support iteration, so don't use it.
153+
// eslint-disable-next-line no-in-operator
154+
return typeof Set !== "undefined" && "entries" in Set.prototype ? Set : undefined;
155+
}
156+
157+
/**
158+
* Returns the native WeakMap implementation if it is available.
159+
*/
160+
export function tryGetNativeWeakMap(): WeakMapConstructor | undefined {
161+
return typeof WeakMap !== "undefined" ? WeakMap : undefined;
162+
}
163+
164+
/**
165+
* Returns the native WeakSet implementation if it is available.
166+
*/
167+
export function tryGetNativeWeakSet(): WeakSetConstructor | undefined {
168+
return typeof WeakSet !== "undefined" ? WeakSet : undefined;
169+
}
89170
}
90171

91172
/* @internal */
92173
namespace ts {
93174
export const emptyArray: never[] = [] as never[];
94175

95176
export const Map: MapConstructor = tryGetNativeMap() || (() => {
96-
// NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
177+
// NOTE: ts.createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
97178
if (typeof createMapShim === "function") {
98179
return createMapShim();
99180
}
100181
throw new Error("TypeScript requires an environment that provides a compatible native Map implementation.");
101182
})();
102183

184+
export const Set: SetConstructor = tryGetNativeSet() || (() => {
185+
// NOTE: ts.createSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
186+
if (typeof createSetShim === "function") {
187+
return createSetShim();
188+
}
189+
throw new Error("TypeScript requires an environment that provides a compatible native Set implementation.");
190+
})();
191+
192+
export const WeakMap: WeakMapConstructor = tryGetNativeWeakMap() || (() => {
193+
// NOTE: ts.createWeakMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
194+
if (typeof createWeakMapShim === "function") {
195+
return createWeakMapShim();
196+
}
197+
throw new Error("TypeScript requires an environment that provides a compatible native WeakMap implementation.");
198+
})();
199+
200+
export const WeakSet: WeakSetConstructor = tryGetNativeWeakSet() || (() => {
201+
// NOTE: ts.createWeakSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
202+
if (typeof createWeakSetShim === "function") {
203+
return createWeakSetShim();
204+
}
205+
throw new Error("TypeScript requires an environment that provides a compatible native WeakSet implementation.");
206+
})();
207+
103208
/** Create a new map. */
104-
export function createMap<T>(): Map<T> {
105-
return new Map<T>();
209+
export function createMap<T>(): Map<T>;
210+
export function createMap<K, V>(): ESMap<K, V>;
211+
export function createMap<K, V>(): ESMap<K, V> {
212+
return new Map<K, V>();
106213
}
107214

108215
/** Create a new map from an array of entries. */
109-
export function createMapFromEntries<T>(entries: [string, T][]): Map<T> {
110-
const map = createMap<T>();
216+
export function createMapFromEntries<T>(entries: readonly [string, T][]): Map<T>;
217+
export function createMapFromEntries<K, V>(entries: readonly [K, V][]): ESMap<K, V>;
218+
export function createMapFromEntries<K, V>(entries: readonly [K, V][]): ESMap<K, V> {
219+
const map = createMap<K, V>();
111220
for (const [key, value] of entries) {
112221
map.set(key, value);
113222
}
@@ -116,7 +225,7 @@ namespace ts {
116225

117226
/** Create a new map from a template object is provided, the map will copy entries from it. */
118227
export function createMapFromTemplate<T>(template: MapLike<T>): Map<T> {
119-
const map: Map<T> = new Map<T>();
228+
const map: Map<T> = new Map<string, T>();
120229

121230
// Copies keys/values from template. Note that for..in will not throw if
122231
// template is undefined, and instead will just exit the loop.
@@ -129,6 +238,18 @@ namespace ts {
129238
return map;
130239
}
131240

241+
export function createSet<T>(): Set<T> {
242+
return new Set<T>();
243+
}
244+
245+
export function createSetFromValues<T>(values: readonly T[]): Set<T> {
246+
const set = createSet<T>();
247+
for (const value of values) {
248+
set.add(value);
249+
}
250+
return set;
251+
}
252+
132253
export function length(array: readonly any[] | undefined): number {
133254
return array ? array.length : 0;
134255
}

src/compiler/utilities.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace ts {
3636

3737
/** Create a new escaped identifier map. */
3838
export function createUnderscoreEscapedMap<T>(): UnderscoreEscapedMap<T> {
39-
return new Map<T>() as UnderscoreEscapedMap<T>;
39+
return new Map<string, T>() as UnderscoreEscapedMap<T>;
4040
}
4141

4242
export function hasEntries(map: ReadonlyUnderscoreEscapedMap<any> | undefined): map is ReadonlyUnderscoreEscapedMap<any> {

0 commit comments

Comments
 (0)