Skip to content

Re-exporting namespace declarations in ES6 ambient declaration #4336

Open

Description

Right now many declaration files take this form:

declare namespace MyLib {

}

declare module 'myLib' {
  export = MyLib;
}

However, there doesn't seem to be an equivalent for ES6 modules:

declare module 'myLib' {
  export default MyLib; // this works for exporting the default, but other exported items in MyLib are not considered to be named exports of the ES6 module
  export * from MyLib; // this is essentially what I'm trying to accomplish
}

Activity

  1. mhegazy commented on Aug 17, 2015

    @mhegazy
    Contributor

    so why use export = then?

  2. jbrantly commented on Aug 17, 2015

    @jbrantly
    Author

    Mohamed Hegazy (@mhegazy) Sorry, I didn't understand your comment. Could you clarify?

    The export = MyLib pattern is useful for making a module from a namespace without having to actually redeclare everything. The goal of the issue, in words, would be to allow making an ES6 module from a namespace without having to actually redeclare everything.

  3. mhegazy commented on Aug 17, 2015

    @mhegazy
    Contributor

    We have talked about 1. allowing external modules to access the global namespace (#4166) and 2. extending export * to namespaces. none has enough traction though.

    what is the proposal here?

  4. jbrantly commented on Aug 17, 2015

    @jbrantly
    Author

    I don't think #4166 applies here. I'm not trying to access the global namespace. I'm trying to re-export the exported declarations of an existing namespace.

    Extending export * to namespaces might be applicable but it's hard to tell without more context on what you mean.

    As far as a proposal, I would suggest new syntax for ambient ES6 module declarations which allows re-exporting declarations from an existing namespace. Let me first illustrate with an example that already works:

    declare module "someLib" {
      export var a: string;
    }
    
    declare module "myLib" {
      export * from "someLib";
      export var b: string;
    }

    In that example, export * from "someLib" re-exports all exports from "someLib". Copied from #2242:

    An export * declaration can be used to re-export all exports of another module. This is useful for creating modules that aggregate the exports of several other modules.

    export function transform(s: string): string { ... }
    export * from "./mod1";
    export * from "./mod2";

    An export * doesn't re-export default exports or exports with names that are already exported from the current module. For example, the transform export in the module above hides any transform export in the re-exported modules.

    My proposal would be the take that same concept and allow it for namespaces as well:

    declare namespace SomeLib {
      export var a: string;
    }
    
    declare module "myLib" {
      export * from SomeLib; // <-- this is the new syntax
      export var b: string;
    }

    Looking at the spec, I don't think there's anything exported from a namespace that can't also be exported from a module. In fact, namespace seems to be a subset of module:

    AmbientExternalModuleElement:
      AmbientModuleElement
      ExportAssignment
      export ExternalImportDeclaration
    

    Assuming that's the case, export * from SomeLib seems reasonable.

    The use case for this is to simplify writing declaration files for existing ES6 libraries (see #4337).

  5. e-cloud commented on Dec 3, 2016

    @e-cloud
    Contributor

    (Re)exporting namespaces or interfaces is really reasonable.


    current status

    You can use the follow style:

    a.d.ts

    export = class A {
    }

    index.d.ts

    export import A = require('./a')

    However, you can't use it in a namespace which is incompatible with scene 3

    And it usage is quite limited.


    possible scene of re-exporting

    For example, I may define several definiation files, assume the library name is MyLib:

    a.d.ts

    export function AFunc(): void

    b.d.ts

    export function BFunc(): void

    scene 1 (re-export namespace as sub namespace)

    index.d.ts (MyLib definition)

    export * as LibA from './a'
    export * as LibB from './b'

    index.tests.ts

    import * as MyLib from 'MyLib'
    MyLib.LibA.AFunc()
    MyLib.LibB.BFunc()

    scene 2 (merge the namespaces)

    index.d.ts (MyLib definition)

    export * from './a'
    export * from './b'

    index.tests.ts

    import * as MyLib from 'MyLib'
    MyLib.AFunc()
    MyLib.BFunc()

    scene 3 (normal for node.js modules)

    index.d.ts (MyLib definition)

    declare namespace MyLib {
        export * as LibA from './a'
        export * as LibB from './b'
    }
    
    export = MyLib

    index.tests.ts

    import MyLib = require('MyLib')
    MyLib.LibA.AFunc()
    MyLib.LibB.BFunc()

    scene 4 (like scene2 in scene3)

    ...

  6. raveclassic commented on Jan 16, 2017

    @raveclassic

    Any considerations on this?
    I'm trying to expose some of the codebase with es6-modules as a global object. This object is constructed from nested namespaces. The problem is that most of the things to be exposed are in es6 modules and I just can't reexport them from namespace.

    Something like the following would be extremely useful:

    //foo.ts
    export enum Foo {
      Foo,
      Bar
    }
    
    export type TFoo = Foo | undefined;
    
    export const FOO = 'FOO';
    
    //global.ts
    export namespace TEST {
      export const SOME = 'SOME';
      export namespace Nested {
        export * from './foo.ts'; //error here
      }
    }
  7. FlippieCoetser commented on Feb 19, 2017

    @FlippieCoetser

    (re)exporting namespaces using existing export syntax would be helpful.

    Exporting

    export namespace NameSpace {
        export {foo} from './foo'
    }

    Allowing the above, could it help getting rid of the annoying Export declaration is not permitted in a namespace error?

    Would love to logically organise a large component using namespace and still maintain the ability to expose needed members to the outside...

  8. G07cha commented on Jun 18, 2017

    @G07cha

    I'm currently using next workaround for interfaces:

    import {foo} from './foo'
    
    export namespace NameSpace {
        export interface Foo extends foo {}
    }

    This should work for classes as well, the only drawback is the fact that you need to rename variables.

  9. tdeekens commented on Jan 3, 2018

    @tdeekens

    I would also align typings with small modules while re exporting them in a accumulating bigger module.

  10. Griffork commented on Feb 19, 2021

    @Griffork

    Oh! Ok! I came across this issue looking for a solution to this problem, but found later in my travels that there is actually a solution now.
    I found 4529 which states that you can do export import Name = Value.

    Like so:

    import FooTemp = Foo;
    export namespace a {
        export import Foo = FooTemp;
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions