-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Are namespaces legacy? #30994
Comments
@DanielRosenwasser Was quoted saying:
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:
|
The TypeScript goal 11 states:
So I would not expect them to go away. |
I guess you're referring to babel/babel#8244 (comment) ? 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 |
@RyanCavanaugh dotansimha/graphql-code-generator#643 (comment) Is this really happening on Typescript?
Why should we lose this ability? I can just import this as
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. |
@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 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 |
@weswigham Is 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) |
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: |
I had read the comments at babel/babel#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: babel/babel#9785 Hopefully this will be available in next release. |
For our team, we can stop to use namespace once if class C { }
namespace C { interface I { } }
const i: C.I = /**/ ; |
Extension methods (C#) are missed in TypeScript. 😅 |
@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 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/ |
I've experienced many times that people like myself struggle a lot with ts-namespaces and es-modules, especially when it comes down to try to work with both in the same project. I did some "structured" fiddling and found that on the Javascript-side after compilation, things appear to be quite simple, but can't be done in a standard way with typescript before compilation. I wrote about it here and I'd be happy to see some comments on that before I start creating a plugin or extension, that tweaks the compiled output. I expect to be missing some important parts... |
If I put another benefit of using namespace like @shrinktofit said:
But unfortunately, there are some discourages structuring your project in this way:
|
If your SPA on Angular needs to talk to multiple backends from different vendors, you will see greater benefits of using namespaces than using modules, especially you have the client API codes generated for each backend, namespaces gives you clearer readability and higher abstraction, since namespaces could be more semantically abstract than modules. |
I have let's say the following interfaces: // user.ts
export interface Status {
isActive: boolean;
}
// account.ts
export interface Status {
lastLogin: Date
} And I don't want to have name collision while trying to import/auto-import With namespace like: namespace User {
export interface Status { ... }
}
namespace Account {
export namespace Status { ... }
} I am sure I won't import wrong import User from 'path-to-user-namespace';
let status = User.Status; So If I use |
...no, not really, because you can do the same thing with actual modules. Eg, import * as User from "./user";
import * as Account from "./account";
export { User, Account }; |
Exactly, I am aware of that. The only downside of that - |
I’m trying to understand why namespaces shouldn’t be used to improve encapsulation, such as the following case in which I have some complex functionality in a server-side app that I want to spread across multiple files, and want to make public only a small subset of the members that should be available to other members of the namespace. (and forgive me if the code is not 100% correct - I’ve never actually used namespaces, I’m just looking into whether I should or not). // calculator.ts
namespace Pricing {
export class Calculator {
export function methodIWantPublic {...}
function calculatorMethodAvailableOnlyInNamespace {
const result = helperMethodAvailableOnlyInNamespace();
}
}
}
// pricing-utils.ts
namespace Pricing {
class Utils {
function helperMethodAvailableOnlyInNamespace {...}
// lots of other methods I don't want to be used outside of Pricing
}
}
// consumer-code.ts
class SomeNonPricingSpecificClass {
const result = Pricing.methodIWantPublic();
} Is there a way to do this on the server with ES6 Modules? What’s the benefit to that approach vs this one? |
If you are using ES6 modules, you just don't export the things you don't want to share. It works the same way if I'm understanding the question correctly. |
@DanielRosenwasser thanks for the reply. But how would that work with local modules (not on NPM) that have multiple files? How would I keep the consumer code from being able to access a method in a helper (for example) that another file in the module needed access to? |
You could create a folder that provides utilities for a module. The module can pick-and-choose what it wants to export. If something imports from
There are variations of this, but that's about it. If you have multiple projects, you can control the exposed API shape with There's a bunch of options, and at the end of the day your namespaces didn't give any more encapsulation than modules without those options. Someone can always reach into a namespace and grab an exported function - it's just that maybe it seemed suspicious to do so. If at the end of the day you still want to use namespaces, that's fine. TypeScript won't be removing them any time soon, though just be aware that you're swimming against the current these days. The broader community is not leveraging them anymore, and not all tooling may choose to put in the work to support them. |
@be aware that you're swimming against the current these days. The broader community is not leveraging them anymore, and not all tooling may choose to put in the work to support them. Be aware which current you are in. A few years ago, The Babal's team had made up their mind not to support namespace, however, eventually they had changed their mind and supported namespace at babel/babel#9785 . Yes, it is technically possible that you can survive with modules only and without namespace even in developing complex business applications with multiple layers, tiers and brokers, just as you can survive with structural programming without OOP. However, it is more viable to have namespaces, kind of managed complexity, for managing unmanaged complexity. With namespaces, some dev tools could be easier to develop, for example, service to client code generators, and the codes generated are overall much better. So application developers could live better. |
Could you give any thoughs about that #30994 (comment) ? |
Good points @zijianhuang.
@DanielRosenwasser My understanding is that, in my namespace example above, the |
I'm in agreement that namespaces have valid use cases. In addition to the points given above (converting namespaces to modules could break encapsulation), my reason is that I would like to keep related code properly encapsulated together as much as possible, and would rather avoid having them split and cluttered across many files. |
I use namespaces all the time to nest interfaces within classes and I don't see modules improving the ergonomics of that at all. True declaration merging is not possible with modules as far as I can see. The nice thing about the code using namespaces below is that, in a large project, the types export class Yada {
doStuff(stuff: Yada.Stuff) {
console.log(`Doing stuff to ${stuff.thing}...`);
}
}
export namespace Yada {
export interface Stuff {
thing: string;
}
} To use this in any file we naturally do I tried testing this out with modules over at playcode.io and codesandbox.io with this code: // File: Foo.ts
export default class Foo {
doStuff(stuff: Stuff) {
console.log(`Doing stuff to ${stuff.thing}...`);
}
}
export interface Stuff {
thing: string;
} And in another file: // File: script.ts, index.tsx, etc:
import * as Foo from "./Foo";
// NOTE: I kinda hate the "import * as Foo" style here because there is no autocomplete for the import name.
function doesNotWork() {
const foo = new Foo(); // Foo is not a class.
foo.doStuff({ thing: "the thing" });
}
function worksButUgly() {
const foo = new Foo.default(); // This is garbage.
foo.doStuff({ thing: "the thing" });
} Module augmentation is not a replacement and re-exporting things to create the nesting structure in a completely separate file which has to import and re-export things is also complete garbage IMO. Maybe modules are better when you're building a library that you'll publish on NPM but for large internal projects where most of the code lives in the project, I'm going to continue to use namespaces for nesting types. |
A student of mine struggled again using namespaces and modules together. I don't see the point why this can't be done out of the box. Referring to my article, it would be nice to hear from the TypeScript-Team to understand what I'm missing. |
Note, module declarations, and module expressions may come to ECMAScript in a few years - those should be able to further replace some use cases for namespaces mentioned earlier in this thread. |
It's silly that namespaces in TS are (as I've found out) evidently "frowned upon". It's like one of those countless no no's that dogmatically permeate software development. Namespaces are ultimately just for names (shockingly). They are a natural organizational facility for identifiers. That's why so many languages have them. They don't have to compete with ES modules, they can compliment them. Though ES imports offer aliasing the core issue is that ES modules being designed for JS, a dynamically typed language, are about exporting/importing values. With values you can implement namespacing with regular objects, just setting properties to values and so on. But you cannot handle a type this way because it is not a value nor existent at runtime. In this case we can use TypeScript namespaces. Don't listen to the haters TypeScript! Namespaces forever! |
I use namespaces to extend some interfaces. For example, add functions to normalize and transform data. As we know, interfaces are just types: they will not generate runnable code on runtime. But, if you add a namespace with the same name, in the same file, you can export functions that you can call as a static method of an interface. export interface IUserData {
// ...
}
export namespace IUserData {
export const defaulted = (data: Partial<IUserData> = {}): IUserData => ...
} import { IUserData } from '@/data/user.data.ts';
// ...
IUserData.defaulted(reponse.data); This is useful when you have a lot of models and want to use some in the same file. If you export the |
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.
The text was updated successfully, but these errors were encountered: