From 737fb7f9b5f98d255a9e33f5f363d6deee8dccfb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 5 Feb 2018 11:01:05 -0800 Subject: [PATCH] Only apply global augmentations before globals are available (#21563) (#21595) * Only apply global augmentations before globals are available * Add detailed comment explaining the split of global/nonglobal augmentations * Remove trailing whitespace --- src/compiler/checker.ts | 20 +++++- ...AugmentationDuringSyntheticDefaultCheck.js | 43 +++++++++++++ ...ntationDuringSyntheticDefaultCheck.symbols | 63 +++++++++++++++++++ ...mentationDuringSyntheticDefaultCheck.types | 63 +++++++++++++++++++ ...AugmentationDuringSyntheticDefaultCheck.ts | 34 ++++++++++ 5 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.js create mode 100644 tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.symbols create mode 100644 tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.types create mode 100644 tests/cases/compiler/moduleAugmentationDuringSyntheticDefaultCheck.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7046a1e5fad..d55e7ecef8a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -25201,11 +25201,18 @@ namespace ts { } } + // We do global augmentations seperately from module augmentations (and before creating global types) because they + // 1. Affect global types. We won't have the correct global types until global augmentations are merged. Also, + // 2. Module augmentation instantiation requires creating the type of a module, which, in turn, can require + // checking for an export or property on the module (if export=) which, in turn, can fall back to the + // apparent type of the module - either globalObjectType or globalFunctionType - which wouldn't exist if we + // did module augmentations prior to finalizing the global types. if (augmentations) { - // merge module augmentations. + // merge _global_ module augmentations. // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed for (const list of augmentations) { for (const augmentation of list) { + if (!isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration)) continue; mergeModuleAugmentation(augmentation); } } @@ -25237,6 +25244,17 @@ namespace ts { globalReadonlyArrayType = getGlobalTypeOrUndefined("ReadonlyArray" as __String, /*arity*/ 1); anyReadonlyArrayType = globalReadonlyArrayType ? createTypeFromGenericGlobalType(globalReadonlyArrayType, [anyType]) : anyArrayType; globalThisType = getGlobalTypeOrUndefined("ThisType" as __String, /*arity*/ 1); + + if (augmentations) { + // merge _nonglobal_ module augmentations. + // this needs to be done after global symbol table is initialized to make sure that all ambient modules are indexed + for (const list of augmentations) { + for (const augmentation of list) { + if (isGlobalScopeAugmentation(augmentation.parent as ModuleDeclaration)) continue; + mergeModuleAugmentation(augmentation); + } + } + } } function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) { diff --git a/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.js b/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.js new file mode 100644 index 00000000000..56fc5806325 --- /dev/null +++ b/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.js @@ -0,0 +1,43 @@ +//// [tests/cases/compiler/moduleAugmentationDuringSyntheticDefaultCheck.ts] //// + +//// [index.d.ts] +declare function moment(): moment.Moment; +declare namespace moment { + interface Moment extends Object { + valueOf(): number; + } +} +export = moment; +//// [index.d.ts] +import * as moment from 'moment'; +export = moment; +declare module "moment" { + interface Moment { + tz(): string; + } +} +//// [idx.ts] +import * as _moment from "moment"; +declare module "moment" { + interface Moment { + strftime(pattern: string): string; + } +} +declare module "moment-timezone" { + interface Moment { + strftime(pattern: string): string; + } +} +//// [idx.test.ts] +/// + +import moment = require("moment-timezone"); + + +//// [idx.js] +"use strict"; +exports.__esModule = true; +//// [idx.test.js] +"use strict"; +/// +exports.__esModule = true; diff --git a/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.symbols b/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.symbols new file mode 100644 index 00000000000..81d4012454e --- /dev/null +++ b/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.symbols @@ -0,0 +1,63 @@ +=== tests/cases/compiler/idx.test.ts === +/// + +import moment = require("moment-timezone"); +>moment : Symbol(moment, Decl(idx.test.ts, 0, 0)) + +=== tests/cases/compiler/node_modules/moment/index.d.ts === +declare function moment(): moment.Moment; +>moment : Symbol(moment, Decl(index.d.ts, 0, 0), Decl(index.d.ts, 0, 41), Decl(idx.ts, 0, 34), Decl(index.d.ts, 1, 16)) +>moment : Symbol(moment, Decl(index.d.ts, 0, 0), Decl(index.d.ts, 0, 41)) +>Moment : Symbol(Moment, Decl(index.d.ts, 1, 26)) + +declare namespace moment { +>moment : Symbol(moment, Decl(index.d.ts, 0, 0), Decl(index.d.ts, 0, 41), Decl(idx.ts, 0, 34), Decl(index.d.ts, 1, 16)) + + interface Moment extends Object { +>Moment : Symbol(Moment, Decl(index.d.ts, 1, 26), Decl(idx.ts, 1, 25), Decl(idx.ts, 6, 34), Decl(index.d.ts, 2, 25)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + valueOf(): number; +>valueOf : Symbol(Moment.valueOf, Decl(index.d.ts, 2, 35)) + } +} +export = moment; +>moment : Symbol(moment, Decl(index.d.ts, 0, 0), Decl(index.d.ts, 0, 41)) + +=== tests/cases/compiler/node_modules/moment-timezone/index.d.ts === +import * as moment from 'moment'; +>moment : Symbol(moment, Decl(index.d.ts, 0, 6)) + +export = moment; +>moment : Symbol(moment, Decl(index.d.ts, 0, 6)) + +declare module "moment" { + interface Moment { +>Moment : Symbol(Moment, Decl(index.d.ts, 1, 26), Decl(idx.ts, 1, 25), Decl(idx.ts, 6, 34), Decl(index.d.ts, 2, 25)) + + tz(): string; +>tz : Symbol(Moment.tz, Decl(index.d.ts, 3, 22)) + } +} +=== tests/cases/compiler/idx.ts === +import * as _moment from "moment"; +>_moment : Symbol(_moment, Decl(idx.ts, 0, 6)) + +declare module "moment" { + interface Moment { +>Moment : Symbol(Moment, Decl(index.d.ts, 1, 26), Decl(idx.ts, 1, 25), Decl(idx.ts, 6, 34), Decl(index.d.ts, 2, 25)) + + strftime(pattern: string): string; +>strftime : Symbol(Moment.strftime, Decl(idx.ts, 2, 22), Decl(idx.ts, 7, 22)) +>pattern : Symbol(pattern, Decl(idx.ts, 3, 17)) + } +} +declare module "moment-timezone" { + interface Moment { +>Moment : Symbol(Moment, Decl(index.d.ts, 1, 26), Decl(idx.ts, 1, 25), Decl(idx.ts, 6, 34), Decl(index.d.ts, 2, 25)) + + strftime(pattern: string): string; +>strftime : Symbol(Moment.strftime, Decl(idx.ts, 2, 22), Decl(idx.ts, 7, 22)) +>pattern : Symbol(pattern, Decl(idx.ts, 8, 17)) + } +} diff --git a/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.types b/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.types new file mode 100644 index 00000000000..bfcf9a9e882 --- /dev/null +++ b/tests/baselines/reference/moduleAugmentationDuringSyntheticDefaultCheck.types @@ -0,0 +1,63 @@ +=== tests/cases/compiler/idx.test.ts === +/// + +import moment = require("moment-timezone"); +>moment : { default: () => moment.Moment; } + +=== tests/cases/compiler/node_modules/moment/index.d.ts === +declare function moment(): moment.Moment; +>moment : () => Moment +>moment : any +>Moment : Moment + +declare namespace moment { +>moment : () => Moment + + interface Moment extends Object { +>Moment : Moment +>Object : Object + + valueOf(): number; +>valueOf : () => number + } +} +export = moment; +>moment : () => Moment + +=== tests/cases/compiler/node_modules/moment-timezone/index.d.ts === +import * as moment from 'moment'; +>moment : { default: () => moment.Moment; } + +export = moment; +>moment : { default: () => moment.Moment; } + +declare module "moment" { + interface Moment { +>Moment : Moment + + tz(): string; +>tz : () => string + } +} +=== tests/cases/compiler/idx.ts === +import * as _moment from "moment"; +>_moment : { default: () => _moment.Moment; } + +declare module "moment" { + interface Moment { +>Moment : Moment + + strftime(pattern: string): string; +>strftime : { (pattern: string): string; (pattern: string): string; } +>pattern : string + } +} +declare module "moment-timezone" { + interface Moment { +>Moment : Moment + + strftime(pattern: string): string; +>strftime : { (pattern: string): string; (pattern: string): string; } +>pattern : string + } +} diff --git a/tests/cases/compiler/moduleAugmentationDuringSyntheticDefaultCheck.ts b/tests/cases/compiler/moduleAugmentationDuringSyntheticDefaultCheck.ts new file mode 100644 index 00000000000..a0402aece9d --- /dev/null +++ b/tests/cases/compiler/moduleAugmentationDuringSyntheticDefaultCheck.ts @@ -0,0 +1,34 @@ +// @noImplicitReferences: true +// @esModuleInterop: true +// @filename: node_modules/moment/index.d.ts +declare function moment(): moment.Moment; +declare namespace moment { + interface Moment extends Object { + valueOf(): number; + } +} +export = moment; +// @filename: node_modules/moment-timezone/index.d.ts +import * as moment from 'moment'; +export = moment; +declare module "moment" { + interface Moment { + tz(): string; + } +} +// @filename: idx.ts +import * as _moment from "moment"; +declare module "moment" { + interface Moment { + strftime(pattern: string): string; + } +} +declare module "moment-timezone" { + interface Moment { + strftime(pattern: string): string; + } +} +// @filename: idx.test.ts +/// + +import moment = require("moment-timezone");