Skip to content

Commit a8c5fe1

Browse files
committed
Add full implemention of Map and Set to shims
1 parent cefee94 commit a8c5fe1

File tree

9 files changed

+839
-316
lines changed

9 files changed

+839
-316
lines changed

src/compiler/core.ts

+24-8
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,17 @@ namespace ts {
55
export const emptyArray: never[] = [] as never[];
66

77
/** Create a new map. */
8-
export function createMap<T>(): Map<T> {
9-
return new Map<T>();
8+
export function createMap<T>(): Map<T>;
9+
export function createMap<K, V>(): ESMap<K, V>;
10+
export function createMap<K, V>(): ESMap<K, V> {
11+
return new Map<K, V>();
1012
}
1113

1214
/** Create a new map from an array of entries. */
13-
export function createMapFromEntries<T>(entries: [string, T][]): Map<T> {
14-
const map = createMap<T>();
15+
export function createMapFromEntries<T>(entries: readonly [string, T][]): Map<T>;
16+
export function createMapFromEntries<K, V>(entries: readonly [K, V][]): ESMap<K, V>;
17+
export function createMapFromEntries<K, V>(entries: readonly [K, V][]): ESMap<K, V> {
18+
const map = createMap<K, V>();
1519
for (const [key, value] of entries) {
1620
map.set(key, value);
1721
}
@@ -20,7 +24,7 @@ namespace ts {
2024

2125
/** Create a new map from a template object is provided, the map will copy entries from it. */
2226
export function createMapFromTemplate<T>(template: MapLike<T>): Map<T> {
23-
const map: Map<T> = new Map<T>();
27+
const map: Map<T> = new Map<string, T>();
2428

2529
// Copies keys/values from template. Note that for..in will not throw if
2630
// template is undefined, and instead will just exit the loop.
@@ -33,6 +37,18 @@ namespace ts {
3337
return map;
3438
}
3539

40+
export function createSet<T>(): Set<T> {
41+
return new Set<T>();
42+
}
43+
44+
export function createSetFromValues<T>(values: readonly T[]): Set<T> {
45+
const set = createSet<T>();
46+
for (const value of values) {
47+
set.add(value);
48+
}
49+
return set;
50+
}
51+
3652
export function length(array: readonly any[] | undefined): number {
3753
return array ? array.length : 0;
3854
}
@@ -1573,7 +1589,7 @@ namespace ts {
15731589
* Case-sensitive comparisons compare both strings one code-point at a time using the integer
15741590
* value of each code-point after applying `toUpperCase` to each string. We always map both
15751591
* strings to their upper-case form as some unicode characters do not properly round-trip to
1576-
* lowercase (such as `` (German sharp capital s)).
1592+
* lowercase (such as `ẞ` (German sharp capital s)).
15771593
*/
15781594
export function equateStringsCaseInsensitive(a: string, b: string) {
15791595
return a === b
@@ -1631,7 +1647,7 @@ namespace ts {
16311647
* Case-insensitive comparisons compare both strings one code-point at a time using the integer
16321648
* value of each code-point after applying `toUpperCase` to each string. We always map both
16331649
* strings to their upper-case form as some unicode characters do not properly round-trip to
1634-
* lowercase (such as `ẞ` (German sharp capital s)).
1650+
* lowercase (such as `ẞ` (German sharp capital s)).
16351651
*/
16361652
export function compareStringsCaseInsensitive(a: string, b: string) {
16371653
if (a === b) return Comparison.EqualTo;
@@ -1703,7 +1719,7 @@ namespace ts {
17031719
//
17041720
// For case insensitive comparisons we always map both strings to their
17051721
// upper-case form as some unicode characters do not properly round-trip to
1706-
// lowercase (such as `ẞ` (German sharp capital s)).
1722+
// lowercase (such as `ẞ` (German sharp capital s)).
17071723
return (a, b) => compareWithCallback(a, b, compareDictionaryOrder);
17081724

17091725
function compareDictionaryOrder(a: string, b: string) {

src/compiler/corePublic.ts

+151-31
Original file line numberDiff line numberDiff line change
@@ -23,53 +23,82 @@ namespace ts {
2323
}
2424

2525
/** ES6 Map interface, only read methods included. */
26-
export interface ReadonlyMap<T> {
27-
get(key: string): T | undefined;
28-
has(key: string): boolean;
29-
forEach(action: (value: T, key: string) => void): void;
26+
export interface ReadonlyESMap<K, V> {
27+
get(key: K): V | undefined;
28+
has(key: K): boolean;
29+
forEach(action: (value: V, key: K) => void): void;
3030
readonly size: number;
31-
keys(): Iterator<string>;
32-
values(): Iterator<T>;
33-
entries(): Iterator<[string, T]>;
31+
keys(): Iterator<K>;
32+
values(): Iterator<V>;
33+
entries(): Iterator<[K, V]>;
3434
}
3535

3636
/** ES6 Map interface. */
37-
export interface Map<T> extends ReadonlyMap<T> {
38-
set(key: string, value: T): this;
39-
delete(key: string): boolean;
37+
export interface ESMap<K, V> extends ReadonlyESMap<K, V> {
38+
set(key: K, value: V): this;
39+
delete(key: K): boolean;
4040
clear(): void;
4141
}
4242

43+
/** ES6 Map interface, only read methods included. */
44+
export interface ReadonlyMap<T> extends ReadonlyESMap<string, T> {
45+
}
46+
47+
/** ES6 Map interface. */
48+
export interface Map<T> extends ESMap<string, T>, ReadonlyMap<T> {
49+
}
50+
4351
/* @internal */
4452
export interface MapConstructor {
4553
// eslint-disable-next-line @typescript-eslint/prefer-function-type
46-
new <T>(): Map<T>;
54+
new <K, V>(): ESMap<K, V>;
55+
}
56+
57+
export interface ReadonlySet<T> {
58+
readonly size: number;
59+
has(value: T): boolean;
60+
forEach(action: (value: T, key: T) => void): void;
61+
keys(): Iterator<T>;
62+
values(): Iterator<T>;
63+
entries(): Iterator<[T, T]>;
64+
}
65+
66+
export interface Set<T> extends ReadonlySet<T> {
67+
add(value: T): this;
68+
delete(value: T): boolean;
69+
clear(): void;
4770
}
4871

49-
/**
50-
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
51-
*/
5272
/* @internal */
53-
export function tryGetNativeMap(): MapConstructor | undefined {
54-
// Internet Explorer's Map doesn't support iteration, so don't use it.
55-
// Natives
56-
// NOTE: TS doesn't strictly allow in-line declares, but if we suppress the error, the declaration
57-
// is still used for typechecking _and_ correctly elided, which is out goal, as this prevents us from
58-
// needing to pollute an outer scope with a declaration of `Map` just to satisfy the checks in this function
59-
//@ts-ignore
60-
declare const Map: (new <T>() => Map<T>) | undefined;
61-
// eslint-disable-next-line no-in-operator
62-
return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined;
73+
export interface SetConstructor {
74+
// eslint-disable-next-line @typescript-eslint/prefer-function-type
75+
new <T>(): Set<T>;
76+
}
77+
78+
export interface WeakMap<K extends object, V> {
79+
get(key: K): V | undefined;
80+
has(key: K): boolean;
81+
set(key: K, value: V): this;
82+
delete(key: K): boolean;
6383
}
6484

6585
/* @internal */
66-
export const Map: MapConstructor = tryGetNativeMap() || (() => {
67-
// NOTE: createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
68-
if (typeof createMapShim === "function") {
69-
return createMapShim();
70-
}
71-
throw new Error("TypeScript requires an environment that provides a compatible native Map implementation.");
72-
})();
86+
export interface WeakMapConstructor {
87+
// eslint-disable-next-line @typescript-eslint/prefer-function-type
88+
new <K extends object, V>(): WeakMap<K, V>;
89+
}
90+
91+
export interface WeakSet<T extends object> {
92+
has(key: T): boolean;
93+
add(key: T): this;
94+
delete(key: T): boolean;
95+
}
96+
97+
/* @internal */
98+
export interface WeakSetConstructor {
99+
// eslint-disable-next-line @typescript-eslint/prefer-function-type
100+
new <T extends object>(): WeakSet<T>;
101+
}
73102

74103
/** ES6 Iterator type. */
75104
export interface Iterator<T> {
@@ -93,4 +122,95 @@ namespace ts {
93122
EqualTo = 0,
94123
GreaterThan = 1
95124
}
125+
126+
namespace NativeCollections {
127+
declare const Map: MapConstructor | undefined;
128+
declare const Set: SetConstructor | undefined;
129+
declare const WeakMap: WeakMapConstructor | undefined;
130+
declare const WeakSet: WeakSetConstructor | undefined;
131+
132+
export function tryGetNativeMap(): MapConstructor | undefined {
133+
// Internet Explorer's Map doesn't support iteration, so don't use it.
134+
// eslint-disable-next-line no-in-operator
135+
return typeof Map !== "undefined" && "entries" in Map.prototype ? Map : undefined;
136+
}
137+
138+
export function tryGetNativeSet(): SetConstructor | undefined {
139+
// Internet Explorer's Set doesn't support iteration, so don't use it.
140+
// eslint-disable-next-line no-in-operator
141+
return typeof Set !== "undefined" && "entries" in Set.prototype ? Set : undefined;
142+
}
143+
144+
export function tryGetNativeWeakMap(): WeakMapConstructor | undefined {
145+
return typeof WeakMap !== "undefined" ? WeakMap : undefined;
146+
}
147+
148+
export function tryGetNativeWeakSet(): WeakSetConstructor | undefined {
149+
return typeof WeakSet !== "undefined" ? WeakSet : undefined;
150+
}
151+
}
152+
153+
/**
154+
* Returns the native Map implementation if it is available and compatible (i.e. supports iteration).
155+
*/
156+
/* @internal */
157+
export function tryGetNativeMap() {
158+
return NativeCollections.tryGetNativeMap();
159+
}
160+
161+
/**
162+
* Returns the native Set implementation if it is available and compatible (i.e. supports iteration).
163+
*/
164+
/* @internal */
165+
export function tryGetNativeSet() {
166+
return NativeCollections.tryGetNativeSet();
167+
}
168+
169+
/**
170+
* Returns the native WeakMap implementation if it is available.
171+
*/
172+
/* @internal */
173+
export function tryGetNativeWeakMap() {
174+
return NativeCollections.tryGetNativeWeakMap();
175+
}
176+
177+
/**
178+
* Returns the native WeakSet implementation if it is available.
179+
*/
180+
/* @internal */
181+
export function tryGetNativeWeakSet() {
182+
return NativeCollections.tryGetNativeWeakSet();
183+
}
184+
185+
export const Map: MapConstructor = tryGetNativeMap() || (() => {
186+
// NOTE: ts.createMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
187+
if (typeof createMapShim === "function") {
188+
return createMapShim();
189+
}
190+
throw new Error("TypeScript requires an environment that provides a compatible native Map implementation.");
191+
})();
192+
193+
export const Set: SetConstructor = tryGetNativeSet() || (() => {
194+
// NOTE: ts.createSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
195+
if (typeof createSetShim === "function") {
196+
return createSetShim();
197+
}
198+
throw new Error("TypeScript requires an environment that provides a compatible native Set implementation.");
199+
})();
200+
201+
export const WeakMap: WeakMapConstructor = tryGetNativeWeakMap() || (() => {
202+
// NOTE: ts.createWeakMapShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
203+
if (typeof createWeakMapShim === "function") {
204+
return createWeakMapShim();
205+
}
206+
throw new Error("TypeScript requires an environment that provides a compatible native WeakMap implementation.");
207+
})();
208+
209+
export const WeakSet: WeakSetConstructor = tryGetNativeWeakSet() || (() => {
210+
// NOTE: ts.createWeakSetShim will be defined for typescriptServices.js but not for tsc.js, so we must test for it.
211+
if (typeof createWeakSetShim === "function") {
212+
return createWeakSetShim();
213+
}
214+
throw new Error("TypeScript requires an environment that provides a compatible native WeakSet implementation.");
215+
})();
96216
}

src/compiler/utilities.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace ts {
2424

2525
/** Create a new escaped identifier map. */
2626
export function createUnderscoreEscapedMap<T>(): UnderscoreEscapedMap<T> {
27-
return new Map<T>() as UnderscoreEscapedMap<T>;
27+
return new Map<string, T>() as UnderscoreEscapedMap<T>;
2828
}
2929

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

0 commit comments

Comments
 (0)