Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow augmentation of re-exported module #12607

Open
christianacca opened this issue Dec 1, 2016 · 15 comments
Open

Allow augmentation of re-exported module #12607

christianacca opened this issue Dec 1, 2016 · 15 comments
Labels
Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Milestone

Comments

@christianacca
Copy link

christianacca commented Dec 1, 2016

I've sadly discovered that it not possible to augment a ES2015 module that is being re-exported. At least not without specifying the full path to the ES2015 that is then being re-exported.

This has significant implications, particularly for library authors and consumers.

An example would probably help clarify.

Suppose as an authored of a library I want to expose a simple surface area from which consumers should import from. I do this by creating an index.ts file that re-exports modules from nested sub-folders like so:

// index.ts
export { AClass } from './path/to/class-a';
export { BClass } from './path/to/class-b';
export { CClass } from './path/to/class-c';

Now a consumer can import like so:

import { AClass, BClass } from 'some-library'; 

The benefits of the above:

  1. As library author I am free to reorganise the modules into sub-folders as the library code base grows without breaking consumers
  2. As library consumer I don't need to have intimate knowledge of how the library organises it's file on disk - instead I have one path to import modules and this is the name of the npm package itself.

Once augmentation enters the picture all of the above benefits are now gone.

This is really bad for everyone.

What can be done about the situation?

Thanks
Christian

@christianacca
Copy link
Author

Also, not being able to augment a re-exported module can cause confusion (eg: #11406) but does not necessarily have an obvious solution as per @RyanCavanaugh comment

@mhegazy
Copy link
Contributor

mhegazy commented Dec 1, 2016

so, is this your library? or a library you are using?

one thing to note, ES2015 does not allow augmenting on the top level of a module anyways. so moving forward the only augmentation allowed at runtime is augmenting export shapes themselves.

@christianacca
Copy link
Author

Hi @mhegazy thanks for the quick reply.

Let's suppose this is a library I'm using that's been authored by someone else.

In reality, I hit this issue whilst writing a demo app of a library that I'm authoring. I spent 2 hours banging my head thinking I was going crazy!

In my case I was trying to augment an interface re-exported from ng-table

@blakeembrey
Copy link
Contributor

blakeembrey commented Dec 1, 2016

There was a similar discussion at #9532 (comment). The only workaround I'm aware of is to augment the specific module instead of the exported one but that has all the caveats you mentioned. E.g. declare module './path/to/class-a'.

@mhegazy mhegazy added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Dec 1, 2016
@christianacca
Copy link
Author

christianacca commented Dec 1, 2016

Yup that is the workaround, but not a particularly satisfying one.

It means the author loses the freedom to rearrange modules inside a package. The consumer also has to know the "internal" paths.

@doliver3
Copy link

I've hit the same problem. It is important to have a solution to this problem.

@endel
Copy link

endel commented Feb 25, 2018

I've just hit this problem too. I'd like to augment THREE module but it's not possible.

Tried many ways, without success, such as:

declare namespace THREE {
    export interface Object3D {/* augmentations */}
}
declare module 'three' {
    export interface Object3D {/* augmentations */}
}
declare module 'three/three-core' {
    export interface Object3D {/* augmentations */}
}

Any workaround for this would be appreciated!

@speigg
Copy link

speigg commented Oct 9, 2018

Ah, this is why it's not working. I'm trying to do the same thing as @endel (augmenting a re-exported class from the package-scoped three module).

EDIT, this worked for me:

declare module 'three/three-core' {
    interface Object3D {/* augmentations */}
}

@speigg
Copy link

speigg commented Feb 14, 2019

I keep running into this issue over again, forgetting that I can't augment re-exported modules. Would be great if this were fixed.

@RyanCavanaugh RyanCavanaugh added Experience Enhancement Noncontroversial enhancements and removed In Discussion Not yet reached consensus labels Apr 1, 2019
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Apr 1, 2019
@RyanCavanaugh
Copy link
Member

The OP is slightly ambiguous so I'll address both.

If the request is to allow the exporting library to create a new type that augments AClass, that's a strong "no" due to our current architecture and potential user confusion.

If the request is that augmentors can augment the exported AClass type as if it were declared upfront in the exporting library, 100% yes, that "should" work (where "should" is defined as "it is effectively feature work to support each possible variant, but we are on board with someone contributing that work"). Effectively, augmenting any type or value through its aliases should be the same as augmenting the type or value at its original declaration site.

@zpdDG4gta8XKpMCd
Copy link

duplicate of #8545?

@mmichlin66
Copy link

mmichlin66 commented Jun 15, 2019

It is important to fix this issue because, as @christianacca said, "the [library] author loses the freedom to rearrange modules inside a package". Notice also the inconsistency: for a consumer of a package, to use a re-exported type you just specify the package name and the type name:

import {Type} from "library"

To augment, however, you have to know where this type definition resides within the source tree of the library:

declare module "library/src/dir1/dir2/file"
{
    interface Type
    {
    }
}

@J-Rojas
Copy link

J-Rojas commented Aug 31, 2021

Very maddening bug... it should be documented at least somewhere the current limitation. I've wasted hours on this bug, finally found this issue to explain some of this.

Three.js is organized is such a way that augmentation will fail as others mentioned.

@avin-kavish
Copy link

Is this still happening?

I think as long as the declared module path and the import path are the same, it works now.

@macarie
Copy link

macarie commented Jul 28, 2022

No, I don't think this is working.

e.g. in https://github.com/pmndrs/valtio, the useSnapshot function is exported in the index file from './react', and

declare module 'valtio' {
  export function useSnapshot<T extends object>(p: T): T
}

does not augment the module, but overwrites it.

Is the above code correct? If it is, are there any known workarounds?

ref: pmndrs/valtio#458

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Experience Enhancement Noncontroversial enhancements Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests