diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index d8017d601adb0..93c295458b4d6 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -326,7 +326,7 @@ namespace ts { // Otherwise, we'll be merging into a compatible existing symbol (for example when // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. - symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name)); + symbol = getOrUpdate(symbolTable, name, name => createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { classifiableNames[name] = name; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d954ea51170a2..228856cadec5c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -518,12 +518,12 @@ namespace ts { function getSymbolLinks(symbol: Symbol): SymbolLinks { if (symbol.flags & SymbolFlags.Transient) return symbol; const id = getSymbolId(symbol); - return symbolLinks[id] || (symbolLinks[id] = {}); + return getOrUpdateArray(symbolLinks, id, () => ({})); } function getNodeLinks(node: Node): NodeLinks { const nodeId = getNodeId(node); - return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 }); + return getOrUpdateArray(nodeLinks, nodeId, () => ({ flags: 0 })); } function isGlobalSourceFile(node: Node) { @@ -8351,7 +8351,7 @@ namespace ts { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); + const cache = getOrUpdateArray>(flowLoopCaches, id, () => createMap()); if (!key) { key = getFlowCacheKey(reference); } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 27b27bc653292..634647bb7c763 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -409,6 +409,23 @@ namespace ts { return hasOwnProperty.call(map, key) ? map[key] : undefined; } + /** + * Gets a property in a Map, or if it doesn't exist, creates and returns it. + * `makeValue` is also passed the key to avoid creating unnecessary closures. + */ + export function getOrUpdate(map: Map, key: string, makeValue: (key: string) => T) { + return map[key] || (map[key] = makeValue(key)); + } + + /** + * Gets a value in an array, or if it doesn't exist, creates and returns it. + * It is the caller's responsibility to ensure that `key` is within the bounds of the array. + * `makeValue` is also passed the key to avoid creating unnecessary closures. + */ + export function getOrUpdateArray(arr: T[], key: number, makeValue: (key: number) => T) { + return arr[key] || (arr[key] = makeValue(key)); + } + /** * Gets the owned, enumerable property keys of a map-like. * diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 035b74230f172..ebc9fea6ce4fb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1086,7 +1086,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return identifiers[text] || (identifiers[text] = text); + return getOrUpdate(identifiers, text, text => text); } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues diff --git a/src/services/services.ts b/src/services/services.ts index 9ef14315cb6e9..917320bb3d7ff 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -990,7 +990,7 @@ namespace ts { } function getDeclarations(name: string) { - return result[name] || (result[name] = []); + return getOrUpdate(result, name, () => []); } function getDeclarationName(declaration: Declaration) {