Skip to content

Provide a way to alias namespaces #10187

Open

Description

@falsandtru

TypeScript should have a way to embed (type) namespaces. In the following case, assigned (embeded) namespace NS.A should have a C type.

TypeScript Version: master

Code

namespace NS_A {
  export class C {
  }
}
namespace NS {
  export var A = NS_A;
  export type A = NS_A;
}
var C = NS.A.C;
type C = NS.A.C;

Expected behavior:

$ node built/local/tsc.js --lib es6 -m commonjs --noEmit index.ts

Actual behavior:

$ node built/local/tsc.js --lib es6 -m commonjs --noEmit index.ts
index.ts(7,19): error TS2304: Cannot find name 'NS_A'.
index.ts(10,13): error TS2305: Module 'NS' has no exported member 'A'.

Activity

  1. changed the title [-]Namespaces that assigned to types don't work with type namespace such as classes and interfaces[/-] [+]Provide a way to alias namespaces[/+] on Aug 6, 2016
  2. RyanCavanaugh commented on Aug 6, 2016

    @RyanCavanaugh
    Member

    I've been thinking about this as well. We have var / const to alias a value, and type to alias a type, but no equivalent for namespace. We could even consider

    namespace ns = foo;

    for establishing namespace aliases.

  3. falsandtru commented on Aug 6, 2016

    @falsandtru
    ContributorAuthor

    Thanks!! I agree its syntax.

    related: #5220

  4. falsandtru commented on Aug 6, 2016

    @falsandtru
    ContributorAuthor

    When extending namespaces, namespace merging is not good for immutability.

    namespace ns = foo;
    // foo is overwritten
    namespace ns {
      export var bar;
    }

    Source namespaces shouldn't be changed by another module. So I think, an extends keyword for namespaces is a good solution.

    // foo is not overwritten
    namespace ns extends foo {
      export var bar;
    }
  5. saschanaz commented on Aug 7, 2016

    @saschanaz
    Contributor

    It seems this existing syntax just works?

    namespace NS_A {
      export class C {
      }
    }
    namespace NS {
      export import A = NS_A;
    }
    var C = NS.A.C;
    type C = NS.A.C;
  6. zpdDG4gta8XKpMCd commented on Aug 7, 2016

    @zpdDG4gta8XKpMCd

    why won't we unify objects and namespaces instead of making them even more separated without a good reason?

    related #8358

  7. falsandtru commented on Aug 7, 2016

    @falsandtru
    ContributorAuthor

    Kagami Sascha Rosylight (@saschanaz) Oh, I didn't know its syntax. My problems were solved. Thanks for the great answer!!

  8. zpdDG4gta8XKpMCd commented on Aug 7, 2016

    @zpdDG4gta8XKpMCd

    as was shown it works fine as long as you separate values/functions from types, unifying them is what we all can benefit from

  9. RyanCavanaugh commented on Aug 22, 2016

    @RyanCavanaugh
    Member

    Looking to understand what the use cases for this are. Some real world code examples would be useful.

  10. falsandtru commented on Aug 22, 2016

    @falsandtru
    ContributorAuthor
  11. RyanCavanaugh commented on Sep 21, 2016

    @RyanCavanaugh
    Member

    #11025 is also a good use case

  12. RyanCavanaugh commented on Sep 26, 2016

    @RyanCavanaugh
    Member

    To explain what's going on in #11025 -- there's no way to alias in the namespace side of an elided module import into a merged identifier.

  13. bfricka commented on Oct 17, 2018

    @bfricka

    Ryan Cavanaugh (@RyanCavanaugh) I know this is pretty old, but I'm currently trying to create better type defs for the popular node-forge library, which does a bunch of aliasing. E.g.:

    forge.pki = forge.pki || {};
    module.exports = forge.pki.rsa = forge.rsa = forge.rsa || {};

    Thus, I'm looking to do something where I can alias like:

    declare module 'node-forge' {
      namespace pki {
        namespace rsa {
          interface GenerateKeyPairOptions {}
          generateKeyPair(opts?: GenerateKeyPairOptions): KeyPair;
        }
      }
      // What I'd like to do:
      // namespace rsa = pki.rsa;
    
      // What I've tried:
      const rsa = pki.rsa; // Access to values from namespace
      type rsa = pki.rsa; // Trying to alias as type to access types (doesn't work)
    }

    When using this lib, I should be able to do the following, but can't access types:

    import {rsa} from 'node-forge';
    
    // TS2503: Cannot find namespace 'rsa'
    const opts: rsa.GenerateKeyPairOptions = {};
    
    // Works fine as aliased value
    rsa.generateKeyPair(); 
  14. hiranya911 commented on Jan 4, 2020

    @hiranya911

    I have a similar use case that involves nested namespaces:

    // index.d.ts
    declare namespace Parent {
    
    }
    
    // other.d.ts
    declare namespace Other {
      // exported members
    }
    

    I'd like to expose everything in Other as a nested namespace of Parent. Something like:

    declare namespace Parent {
      namespace Child = Other;
    }
    

    Today I achieve this by individually re-exporting the members of Other:

    // index.d.ts
    
    import {Other} from './other';
    
    declare namespace Parent {
      namespace Child {
        export import Member1 = Other.Member1;
        export import Member2 = Other.Member2;
        // and so on...
      }
    }
    
  15. pgherveou commented on Jun 24, 2020

    @pgherveou

    stumbled upon this issue, it looks like this feature is supported now
    see doc here https://www.typescriptlang.org/docs/handbook/namespaces.html#aliases
    falsandtru (@falsandtru) if that answer your question you might want to close this issue

  16. falsandtru commented on Jun 24, 2020

    @falsandtru
    ContributorAuthor

    See #10187 (comment) and below.

  17. bakkot commented on Jul 30, 2022

    @bakkot
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) I have another real-world use case, which is working around #48764 (comment).

    Specifically, I have a library which has several classes which are gated behind an init function, like

    export interface Foo {
      method(): void
    };
    interface FooCtor {
      new(): Foo
    };
    
    export function init(): { Foo: FooCtor } {
      class Foo { method() {} }
      return { Foo };
    }

    and as in the above example, the types for instances of the inner classes are exported from the library. The way you're supposed to use it is

    import { init } from 'my-module';
    import type * as MyMod from 'my-module';
    
    let MyMod = init();
    let foo: MyMod.Foo = new MyMod.Foo();

    but this doesn't actually work, because the import type declaration conflicts with the let MyMod declaration.

    I would like to suggest that users write

    import type * as _MyMod from 'my-module';
    namespace MyMod = _MyMod;

    so that the MyMod binding appears only in the type domain and hence would not conflict with the binding of the same name in the value domain, but that isn't possible because there is no way to alias namespaces.

  18. ValeTheVioletMote commented on Aug 2, 2022

    @ValeTheVioletMote

    Here's another use case - because of this problem ( #41567 #41513 ) we can't use import for types from a non-module js file without typescript giving a faulty/broken output. This is being called 'working as intended'. But because of this """feature""", we can no longer simply import a namespace via import NamespaceAlias from "namespace"

    To be fair to the "working as intended" defenders, it is true that we aren't actually truly importing the value. This was actually a workaround to the underlying problem of being unable to alias a namespace.

    In other words, this is no longer valid TS code while not in a module file:

    import A from 'a';
    type B = A.B;

    The proposed workaround was to use import("a")

    But this does not work

    type A = import("a") // Module 'a' does not refer to a type, but is used as a type here. Did you mean 'typeof import('a')'?

    Sure:

    type A = typeof import("a")
    type B = A.B // 'A' only refers to a type, but is being used as a namespace here.

    Thus, there is a need for a namespace alias, otherwise in every single reference to this namespace I'm going to have to replace A. with import("a").

    because this would, presumably, work fine

    namespace A = import("a")
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

    Needs More InfoThe issue still hasn't been fully clarifiedSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions