Typescript: Are namespaces legacy?

Created on 17 Apr 2019  路  17Comments  路  Source: microsoft/TypeScript

Sorry... I know it specifically says not to ask questions here... but I don't want the opinion of random developers on stack exchange... I want to hear from the typescript team directly.

Are namespaces (aka internal modules) legacy and going away?

There is a discussion about Babel7 support for namespaces . _One_ of the arguments for not supporting them is that they are going away in the future anyways. However, I can't seem to find any mention to that in official documentation.

If that is the case, then it really should be mentioned somewhere... Lots of people are relying on them for libraries (because they provide some very convenient features; especially around code generators).

I personally have at least 10 production projects that would need to be heavily refactored if they went away.

FYI @DanielRosenwasser (because you were quoted as saying they were legacy).


Response from TypeScript Team (@RyanCavanaugh)

Namespaces are not being removed from TypeScript! See comments downthread.

Discussion

Most helpful comment

@Jonatthu I'd refer you to my comment upthread. Again: We've never removed any syntax from the language since 1.0 and don't intend to do so in the future.

For anyone reading this thread: Please don't trust internet randos about TypeScript's feature plans! Our roadmap is public anyone saying crazy stuff like that should be able to point to a roadmap entry for it.

All 17 comments

@DanielRosenwasser Was quoted saying:

... namespaces are not a huge loss ...

Which from my interpretation meant that it's not a priority for a system to support namespaces, but nothing to the effect of implicating their deprecation or legacy status.

@Timer was quoted saying they were legacy, which could only be substantiated using third party opinion blog posts.

@Wolvereness sorry yes, didn't mean to put words in @DanielRosenwasser mouth. His quote was used to argue that they are legacy...

Here's the full post for clarity:

TypeScript PM here. Broadly speaking, I feel that

namespaces are not a huge loss; the React community is firmly on-board with ES modules anyway, and that's the direction the TypeScript world is taking.
const enums are often misused. Sure, they can be great for the internals of a library to get some speed improvements, but they generally shouldn't be used outside of that scope.
Merging behavior is marginally useful now that TypeScript 3.1 has the ability to tack properties onto functions. The fact that enums merge is also something I've never seen used out in the wild.
Anyway, @RyanCavanaugh might have some thoughts here too.

The TypeScript goal 11 states:

Do not cause substantial breaking changes from TypeScript 1.0.

So I would not expect them to go away.

I guess you're referring to https://github.com/babel/babel/issues/8244#issuecomment-455839641 ? This person isn't a TypeScript team member and I don't know where they got that idea.

We've never removed any syntax from the language since 1.0 and don't intend to do so in the future.

Yes, I think @Wolvereness captured my intent accurately - in other words, Babel probably shouldn't drastically re-architect itself to support namespaces.

@RyanCavanaugh https://github.com/dotansimha/graphql-code-generator/issues/643#issuecomment-491015913

Is this really happening on Typescript?
Is Typescript team deprecating namespaces?
I think it is really useful for this use case and others that creates multiple types related to a dynamic query such as Graphql gen types.

import { Action } from '@ngrx/store';

export namespace PermissionsActions
{

    export const LOAD_DETAILS: string = '[Permissions] Load Details';
    export const LOAD_DETAILS_FAIL: string = '[Permissions] Load Details Fail';
    export const LOAD_DETAILS_SUCCESS: string = '[Permissions] Load Details Success';

    export class LoadDetails implements Action
    {
        /**
         * The type of the action.
         */
        public readonly type: string = LOAD_DETAILS;

        constructor(public permissionKey: string) { }
    }

    export class LoadDetailsFail implements Action
    {
        /**
         * The type of the action.
         */
        public readonly type: string = LOAD_DETAILS_FAIL;

        constructor() { }
    }

    export class LoadDetailsSuccess implements Action
    {
        /**
         * The type of the action.
         */
        public readonly type: string = LOAD_DETAILS_SUCCESS;

        constructor(public payload: any) { }
    }

    /**
     * Permissions Actions Type Union
     */
    export type Actions =
        | LoadDetails
        | LoadDetailsFail
        | LoadDetailsSuccess
        ;

}

Why should we lose this ability?

I can just import this as

import { PermissionsActions } from '...';
vs
import * as PermissionsActions from '...';

With namespaces I can find all references easily plus we can maintain the same naming convention when we import this kind of types on a ts file.

Really useful on Angular Applications using NGRX and now applications using Graphql Types Generation.

@FredyC

@Jonatthu I'd refer you to my comment upthread. Again: We've never removed any syntax from the language since 1.0 and don't intend to do so in the future.

For anyone reading this thread: Please don't trust internet randos about TypeScript's feature plans! Our roadmap is public anyone saying crazy stuff like that should be able to point to a roadmap entry for it.

Namespaces are probably never going away and, simultaneously, you _probably_ shouldn't use them, since better, more standard and modern patterns exist through the use of modules. If you have code that you feel _needs_ to use a namespace, more power to you and go for it, but most of the time you don't _need_ one, as we now have a different conceptual organizational model recommended for large-scale JS (modules). That's why @DanielRosenwasser said namespaces are not a huge loss - most _new_ TS code should probably never need to use them, and should think hard about if they really _need_ to.

Minimally, we'll never outright remove namespaces because they're incredibly useful for describing type hierarchies and the like of existing cjs libraries - it's just that modern esm-like code probably doesn't need them, since the module itself _is a namespace_ (where nesting is achieved thru reexports).

@weswigham Is namespace merging with class, function and enum a 'good' use of namespaces? (as described in the docs)

I ask because people seem afraid of namespaces these days for some reason and the merging functionality can't easily be achieved by other means. (Functions have nicer semantics these days for adding members, but aside from that)

Is namespace merging with class, function and enum a 'good' use of namespaces?

Sometimes - since we can't recognize ad-hoc attachments of certain kinds of static properties, it might be warranted - in many cases an ad-hoc property on a function or a class static will suffice (unless you need it to contain types), though. Although it's just as valid to question if you really needed the merge to begin with - if, for example, you want to associate a component and its argument type, isn't exporting both of them from the same module sufficient? Why also wrap them in a namespace? There's no point there.

It comes down to this:
If you're considering using namespaces for code organization: _Don't_. Modules have subsumed that role.
If you need functionality that only namespaces can provide: _Do_, but check that it's not equally expressive to express the concept without a namespace (eg, with a class static or function property, or reexported module). It is also bad style to mix namespaces and modules in the same project - it just feels off, since one of the major features of namespaces in the traditional sense is cross-file scope merging, which doesn't occur across modules (since, as I said, the module itself is actually a namespace).

I had read the comments at https://github.com/babel/babel/issues/8244 made by the Babel team as mentioned above.

Complex business fat clients like SPA may contain a lot types in different namespaces. ES module is not going to replace namespaces of TS, since module is 1 to 1 mapping with file. Manually avoiding circular references among files is a nightmare against application programming and overall productivity of app programmers. Namepaces is the best solution for code generation -- all generated by code generator stay in one file.

All types from such generated file are with the namespace prefix, so app programmers could easily tell which types are from generated lib.

I am building a complex business app of Angular SPA with tons of types mapped to the server types of .NET backend. Having namespaces is great for productivity.

And the good thing is, the Babel team may be changing their mind about supporting namespaces: https://github.com/babel/babel/pull/9785
So much thanks to @Wolvereness.

Hopefully this will be available in next release.

Why also wrap them in a namespace?

For our team, we can stop to use namespace once if public type T = and public interface I {} is allowed in class scope. Because sometimes we don't want to expose interfaces/types directly into module scope. Only choice is to use namespace merging:

class C { }
namespace C {  interface I { }  }

const i: C.I = /**/ ;

@weswigham Is namespace merging with class, function and enum a 'good' use of namespaces? (as described in the docs)

Extension methods (C#) are missed in TypeScript. 馃槄

@nelson6e65 , Extension methods are supported in JavaScript thus in TypeScript as well.

@nelson6e65 , Extension methods are supported in JavaScript thus in TypeScript as well.

Really? How? I had to write a wrapper generic class for specific enums to achieve this, but not as extension methods.

Kind of:

// status.ts
export enum Status {
  None,
  Good,
  Other
}

// status extension.ts
export class StatusExtension {
  public static message(this status: Status) {
    return status === Status.Good ? 'All is good' : 'Not that good'
  }
}
import { Status } from './status';
import './status-extension.ts';

const sta = Status.Good;

console.log('Status message:', sta.message());

...similar to C# extension methods.

With the namespace-enum merging you have to call something like Status.message(sta) instead of sta.message()

But currently I have to create a wrapper class:

class StatusWrapper {
  constructor(public readonly value: Status) {}

  message() {
    return this.value=== Status.Good ? 'All is good' : 'Not that good'
  }
}

//

const sta2 = new StatusWrapper(Status.Bad);

console.log(sta2.message());

@nelson6365, prototype, though people should use prototype with cautions. A TS example is here: https://putridparrot.com/blog/extension-methods-in-typescript/

Was this page helpful?
0 / 5 - 0 ratings

Related issues

siddjain picture siddjain  路  3Comments

weswigham picture weswigham  路  3Comments

MartynasZilinskas picture MartynasZilinskas  路  3Comments

blendsdk picture blendsdk  路  3Comments

uber5001 picture uber5001  路  3Comments