mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-23 07:07:09 -05:00
Fix merging module augmentations to pattern ambient modules
This commit is contained in:
@@ -500,6 +500,7 @@ namespace ts {
|
||||
* This is only used if there is no exact match.
|
||||
*/
|
||||
let patternAmbientModules: PatternAmbientModule[];
|
||||
let patternAmbientModuleAugmentations: Map<Symbol> | undefined;
|
||||
|
||||
let globalObjectType: ObjectType;
|
||||
let globalFunctionType: ObjectType;
|
||||
@@ -890,7 +891,7 @@ namespace ts {
|
||||
* Note: if target is transient, then it is mutable, and mergeSymbol with both mutate and return it.
|
||||
* If target is not transient, mergeSymbol will produce a transient clone, mutate that and return it.
|
||||
*/
|
||||
function mergeSymbol(target: Symbol, source: Symbol): Symbol {
|
||||
function mergeSymbol(target: Symbol, source: Symbol, unidirectional = false): Symbol {
|
||||
if (!(target.flags & getExcludedSymbolFlags(source.flags)) ||
|
||||
(source.flags | target.flags) & SymbolFlags.Assignment) {
|
||||
Debug.assert(source !== target);
|
||||
@@ -923,7 +924,9 @@ namespace ts {
|
||||
if (!target.exports) target.exports = createSymbolTable();
|
||||
mergeSymbolTable(target.exports, source.exports);
|
||||
}
|
||||
recordMergedSymbol(target, source);
|
||||
if (!unidirectional) {
|
||||
recordMergedSymbol(target, source);
|
||||
}
|
||||
}
|
||||
else if (target.flags & SymbolFlags.NamespaceModule) {
|
||||
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
|
||||
@@ -1023,7 +1026,22 @@ namespace ts {
|
||||
// obtain item referenced by 'export='
|
||||
mainModule = resolveExternalModuleSymbol(mainModule);
|
||||
if (mainModule.flags & SymbolFlags.Namespace) {
|
||||
mainModule = mergeSymbol(mainModule, moduleAugmentation.symbol);
|
||||
// If we’re merging an augmentation to a pattern ambient module, we want to
|
||||
// perform the merge unidirectionally from the augmentation ('a.foo') to
|
||||
// the pattern ('*.foo'), so that 'getMergedSymbol()' on a.foo gives you
|
||||
// all the exports both from the pattern and from the augmentation, but
|
||||
// 'getMergedSymbol()' on *.foo only gives you exports from *.foo.
|
||||
if (some(patternAmbientModules, module => mainModule === module.symbol)) {
|
||||
const merged = mergeSymbol(moduleAugmentation.symbol, mainModule, /*unidirectional*/ true);
|
||||
if (!patternAmbientModuleAugmentations) {
|
||||
patternAmbientModuleAugmentations = createMap();
|
||||
}
|
||||
// moduleName will be a StringLiteral since this is not `declare global`.
|
||||
patternAmbientModuleAugmentations.set((moduleName as StringLiteral).text, merged);
|
||||
}
|
||||
else {
|
||||
mergeSymbol(mainModule, moduleAugmentation.symbol);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// moduleName will be a StringLiteral since this is not `declare global`.
|
||||
@@ -2391,6 +2409,14 @@ namespace ts {
|
||||
if (patternAmbientModules) {
|
||||
const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleReference);
|
||||
if (pattern) {
|
||||
// If the module reference matched a pattern ambient module ('*.foo') but there’s also a
|
||||
// module augmentation by the specific name requested ('a.foo'), we store the merged symbol
|
||||
// by the augmentation name ('a.foo'), because asking for *.foo should not give you exports
|
||||
// from a.foo.
|
||||
const augmentation = patternAmbientModuleAugmentations && patternAmbientModuleAugmentations.get(moduleReference);
|
||||
if (augmentation) {
|
||||
return getMergedSymbol(augmentation);
|
||||
}
|
||||
return getMergedSymbol(pattern.symbol);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
tests/cases/conformance/ambient/testB.ts(1,22): error TS2305: Module '"*.foo"' has no exported member 'onlyInA'.
|
||||
|
||||
|
||||
==== tests/cases/conformance/ambient/types.ts (0 errors) ====
|
||||
declare module "*.foo" {
|
||||
let everywhere: string;
|
||||
}
|
||||
|
||||
|
||||
==== tests/cases/conformance/ambient/testA.ts (0 errors) ====
|
||||
import { everywhere, onlyInA } from "a.foo";
|
||||
declare module "a.foo" {
|
||||
let onlyInA: number;
|
||||
}
|
||||
|
||||
==== tests/cases/conformance/ambient/testB.ts (1 errors) ====
|
||||
import { everywhere, onlyInA } from "b.foo"; // Error
|
||||
~~~~~~~
|
||||
!!! error TS2305: Module '"*.foo"' has no exported member 'onlyInA'.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//// [tests/cases/conformance/ambient/ambientDeclarationsPatterns_merging1.ts] ////
|
||||
|
||||
//// [types.ts]
|
||||
declare module "*.foo" {
|
||||
let everywhere: string;
|
||||
}
|
||||
|
||||
|
||||
//// [testA.ts]
|
||||
import { everywhere, onlyInA } from "a.foo";
|
||||
declare module "a.foo" {
|
||||
let onlyInA: number;
|
||||
}
|
||||
|
||||
//// [testB.ts]
|
||||
import { everywhere, onlyInA } from "b.foo"; // Error
|
||||
|
||||
|
||||
//// [types.js]
|
||||
//// [testA.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
//// [testB.js]
|
||||
"use strict";
|
||||
exports.__esModule = true;
|
||||
@@ -0,0 +1,26 @@
|
||||
=== tests/cases/conformance/ambient/types.ts ===
|
||||
declare module "*.foo" {
|
||||
>"*.foo" : Symbol("*.foo", Decl(types.ts, 0, 0))
|
||||
|
||||
let everywhere: string;
|
||||
>everywhere : Symbol(everywhere, Decl(types.ts, 1, 5))
|
||||
}
|
||||
|
||||
|
||||
=== tests/cases/conformance/ambient/testA.ts ===
|
||||
import { everywhere, onlyInA } from "a.foo";
|
||||
>everywhere : Symbol(everywhere, Decl(testA.ts, 0, 8))
|
||||
>onlyInA : Symbol(onlyInA, Decl(testA.ts, 0, 20))
|
||||
|
||||
declare module "a.foo" {
|
||||
>"a.foo" : Symbol("a.foo", Decl(testA.ts, 0, 44), Decl(types.ts, 0, 0))
|
||||
|
||||
let onlyInA: number;
|
||||
>onlyInA : Symbol(onlyInA, Decl(testA.ts, 2, 5))
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/ambient/testB.ts ===
|
||||
import { everywhere, onlyInA } from "b.foo"; // Error
|
||||
>everywhere : Symbol(everywhere, Decl(testB.ts, 0, 8))
|
||||
>onlyInA : Symbol(onlyInA, Decl(testB.ts, 0, 20))
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
=== tests/cases/conformance/ambient/types.ts ===
|
||||
declare module "*.foo" {
|
||||
>"*.foo" : typeof import("*.foo")
|
||||
|
||||
let everywhere: string;
|
||||
>everywhere : string
|
||||
}
|
||||
|
||||
|
||||
=== tests/cases/conformance/ambient/testA.ts ===
|
||||
import { everywhere, onlyInA } from "a.foo";
|
||||
>everywhere : string
|
||||
>onlyInA : number
|
||||
|
||||
declare module "a.foo" {
|
||||
>"a.foo" : typeof import("a.foo")
|
||||
|
||||
let onlyInA: number;
|
||||
>onlyInA : number
|
||||
}
|
||||
|
||||
=== tests/cases/conformance/ambient/testB.ts ===
|
||||
import { everywhere, onlyInA } from "b.foo"; // Error
|
||||
>everywhere : string
|
||||
>onlyInA : any
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// @filename: types.ts
|
||||
declare module "*.foo" {
|
||||
let everywhere: string;
|
||||
}
|
||||
|
||||
|
||||
// @filename: testA.ts
|
||||
import { everywhere, onlyInA } from "a.foo";
|
||||
declare module "a.foo" {
|
||||
let onlyInA: number;
|
||||
}
|
||||
|
||||
// @filename: testB.ts
|
||||
import { everywhere, onlyInA } from "b.foo"; // Error
|
||||
Reference in New Issue
Block a user