I'm having a specific typings file which has a modern shape like this.
// something.d.ts
declare function something(...): ...;
declare namespace something {
interface A { ... }
interface B { ... }
}
export = something;
export as namespace something;
I'm trying to consume this module in file which is not a module, so the official module augmentation doesn't work. My task would be to add another function to the something namespace root, and another to the A interface.
I've tried to utilize namespace merging, but it seems like it's not working because instead of merging it thinks that my declaration is the only one, so the original stuff coming from the typings file is not visible at all.
// myGlobalFile.ts
declare namespace something {
function extraFunction();
interface A {
extraFunctionOnA();
}
}
As far as the interface augmentation goes, I've also tried the silly syntax like
// myGlobalFile.ts
interface something.A {
extraFunctionOnA();
}
But of course that's a syntax error so no way it could work, but it represents half of my goal quite well.
I've put this as a question onto SO but I got no answer nor a comment until now. (https://stackoverflow.com/questions/45505975/how-to-augment-a-module-in-global-context)
TypeScript Version: 2.4.2
Expected behavior:
The module can be augmented from global scope.
Actual behavior:
The augmentation actually kind of overwrites the entire module namespace.
I am facing the same issue with lodash module where i can't augment it (https://github.com/Microsoft/types-publisher/issues/367)
@mhegazy sorry for pinging you but could you please give some feedback on any progress on this one? This one blocks us from using several updated typings.
The only way I was able to get this to work with 'lodash' (IIRC, this used to work correctly but I can't find any code that demonstrates that at the moment)
was to create a .d.ts file like the following
```augmentations.d.ts
export {}; // this file needs to be a module
declare module "lodash" {
interface LoDashStatic {
addedToLoDash(): void;
}
}
The augmented `_` is then available in global scope.
```ts
_.addedToLoDash();
const chain = _.chain;
Thank you this is a nice and elegant idea, even has an extra level of separation of concerns. I'm going to try this soon and give some feedback.
you can do something like:
declare module "mod" {
module "lodash" {
interface LoDashStatic {
addedToLoDash(): void;
}
}
}
i think we need to revisit our augmentation vs. re declaration implementation for modules.
@aluanhaddad Unfortunately your idea doesn't work in my case. I need to augment a module's root exported namespace and one of the interfaces inside. To be specific, the library which I'm trying to augment is videojs.
I tried to create a videojs-custom.d.ts file and put this into that, but these added definitions are not recognized. I've tried to add the export keyword everywhere but that made no difference.
// videojs-custom.d.ts
export { }
declare module "videojs" {
namespace videojs {
function getComponent(name: string): any;
function registerComponent(name: string, definition: any);
function extend(target: any, source: any);
function plugin(name: string, constructor: Function);
interface Player {
loadVideo(videoData: any);
}
}
}
That is almost correct but the the reason it doesn't work as you expect is that the merged declaration in @types/videojs
function videojs(id: any, options: videojs.PlayerOptions, ready?: () => void): videojs.Player;
namespace videojs {...}
_is_ exported _as_ the value of the module itself. This means that you need to remove the namespace that wraps the additional members you declare because your augmentation is actually modifying the modules exported value.
export { }
declare module "videojs" {
function getComponent(name: string): any;
function registerComponent(name: string, definition: any);
function extend(target: any, source: any);
function plugin(name: string, constructor: new (...args: any[]) => any);
interface Player {
loadVideo(videoData: any);
}
}
I use the same technique in the LoDash example actually, since LodashStatic is declared inside the namespace _ in @types/lodash but in the augmentation, the merged interface is at top level.
This can be a bit confusing.
Ah I see now, thank you very much for the clarification. Yes this is a bit confusing indeed. Removing the namespace works like a charm. Thanks again.
EDIT: yeah this is mostly covered by https://github.com/Microsoft/TypeScript/issues/18877 and co
EDIT 2: I couldn't get @mhegazy 's variant of wrapping it in declare module "mod" { to work in any case.
This whole thing is weird...
// ts3dutils.d.ts
export interface V3 {
plus(x: V3): V3
}
export as namespace ts3dutils
// ts3dutilsaugment.d.ts
export {}
declare module "./ts3dutils" {
interface V3 {
__magic_type: V3
}
}
// index.ts
/// <reference path="ts3dutils.d.ts" />
/// <reference path="ts3dutilsaugment.d.ts" />
type A = ts3dutils.V3['plus'] // OK!
type X = ts3dutils.V3['__magic_type'] // OK!
BUT if the interface exported as namespace is actual reexported from somewhere else:
// V3.d.ts
export interface V3 {
plus(x: V3): V3
}
// ts3dutils.d.ts
export * from './V3'
export as namespace ts3dutils
// ts3dutilsaugment.d.ts
export {}
declare module "./ts3dutils" {
interface V3 {
__magic_type: V3
}
}
// index.ts
/// <reference path="ts3dutils.d.ts" />
/// <reference path="ts3dutilsaugment.d.ts" />
type A = ts3dutils.V3['plus'] // OK!
// ERROR: [ts] Property '__magic_type' does not exist on type 'V3'.
type X = ts3dutils.V3['__magic_type']
Related to https://github.com/Microsoft/TypeScript/issues/9681 (?) because the underlying issue seems to be that export as namespace namespaces don't behave like normal ones i.e. they can't be merged as expected.
What I was originally trying to do was hack in conditional mapped types. I.e. augment other types so I can do type MappedType<T> = (T & { __magic_type: {} })['__magic_type']
Augmentations have to target the physical module that the export originates from.
There is no way augment by way of a re-export.
This is awkward since it requires taking a hard dependency on something that could be an implementation detail, the location of a re-exported module.
@mhegazy your solution did work for me:
declare module "mod" {
module "lodash" {
interface LoDashStatic {
addedToLoDash(): void;
}
}
}
Although I don't really understand what "mod" really means here and why we have to write a module within a module 馃
the name "mod" is not important here, you can replace it with any other name, what you care about is the "lodash" module augmentation (which has to be nested inside another module to be considered an augmentation).
Personally, I find
// augmentations.ts
export {} // ensure this is a module
declare module "lodash" {
interface LoDashStatic {
addedToLoDash(): void;
}
}
easier to read.
Talked about this with @mhegazy and we couldn't think of a good solution to this besides @aluanhaddad's.
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
@cervengoc Can you provide a sample of consuming the custom video.js component?
I'm new to the concept of Augmentation and tried to extend a video.js component myself, but when consuming the custom module, I'm getting a compiler error:
Property 'getComponent' does not exist on type ...
Most helpful comment
The only way I was able to get this to work with
'lodash'(IIRC, this used to work correctly but I can't find any code that demonstrates that at the moment)was to create a
.d.tsfile like the following```augmentations.d.ts
export {}; // this file needs to be a module
declare module "lodash" {
interface LoDashStatic {
addedToLoDash(): void;
}
}