-
Notifications
You must be signed in to change notification settings - Fork 47
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
consider namespace #167
Comments
I think this is covered by the ideas in |
I'm not sure if it is covered there. Do you mean: // a.js
:{
// I'm adding `export` here as some types might be internal to this scope.
// The usefulness of such "internal" types are debatable,
// and ultimately determined by the type engine.
export namespace A {
export type Foo { ... }
export type Boo { ... }
}
}
// b.js
import: { type A } from './a.js'
const x: A.Foo
const b: A.Boo |
What of this can't be achieved by putting the code in separate files?
This sounds like a UI problem 😕 |
Yes, I would say it is a DX problem. Speaking of which, #188 approach may make this a bit more cumbersome: // foo.ts
export namespace foo {
export type Options = { ... }
}
export function foo(options: foo.Options) {
}
// boo.ts
import { foo } from './foo.js'
const o: foo.Options = {}
foo(o) vs // foo.js
export: {
export namespace foo {
export type Options { ... }
}
}
export function(foo: foo.Options) { ... }
// boo.js
import { foo } from './foo.js'
// implicit import of types?
const o: foo.Options = {}
foo(o)
// or koo.js
import: { type foo } from './foo.js'
import { foo } from './foo.js'
const o: foo.Options = {}
foo(o) Separating type and code into different files is nice, as in |
Another reason for namespace is that flatten list (direct export) is bound to conflict: import { foo, Options as FooOptions } from './foo.js'
import { boo, Options as BooOptions } from './boo.js'
import { koo, Options as KooOptions } from './koo.js' |
I don't understand this example. Isn't that what |
Agreed. |
I would say more than a little. |
TBH I would say it's the kind of problem that IDE developers should have the freedom to address. In order to not create more problems down the road for them, the language should not add something as elaborate as For the sake of Occam's razor, my question stands:
And, conversely, in what context would you export so many definitions from a single file that you need namespaces to organize them? |
e.g.:
😃 Note that The problem is not just from a single file, it's from a particular package, it can expose hundreds of code, functions, and types. So namespace (or This is a general issue of modules. When we move from global namespace ( And really, namespaces is just "type literals" as compare to "object literals" ( |
Not exactly, based on that idea it would be this for the import: : import { type A } from './a.js' In your example, with the type comment stripped, it leaves invalid JavaScript: import; |
Strip the type comments, and that leaves export which is not valid JavaScript. This is valid (strip the whole type comment, and you have empty JS file, which is valid): // foo.js
:{
export namespace foo {
export type Options { ... }
}
}
This has nothing to do with the comment syntax, this is up to the type system.
This would be: // or koo.js
: import { type foo } from './foo.js'
import { foo } from './foo.js' but because // or koo.js
// type import
: import { foo } from './foo.js'
// real runtime import
import { foo } from './foo.js' (You do not need both import statements. The runtime import already includes the type import.) |
This doesn't relate to the syntax. TS can do whatever it wants. #188 is only specifying the comment space.
Indeed, this is preferred over "deprecated"
With something like #188, the language does not add namespaces. You can replace this, :{
namespace { ... }
}
console.log('this line is actual JavaScript') with this :{
blah blah blah whatever this is just a comment
}
console.log('this line is actual JavaScript') and it works the same. It is not the JS language that defines what goes inside the comment in #188, it only defines the comment space. To explain with another example, this code, : import { blah } from './blah.js'
import { foo } from './foo.js' is the same thing as this code in runtime: : rm -rf /
import { foo } from './foo.js' Not sure if there's a limitation with :: rm -rf /
import { foo } from './foo.js' Paste any TypeScript code here, and I'll show the rough equivalent with type comments from 188. |
@trusktr, Yes, there's a limitation with |
I don't really see the distinction. Under the hood, it's all just objects. The packages that you provided as examples already achieve namespacing in a simple and non-confusing way, without the need for a
I can see how these words could be understood in the way you understood them. However this was not their intended meaning. I meant something like "imagine Perhaps it would do me good to apologize for such an ambiguity - but it's not an ambiguity I introduced, and neither did you. This only drives home my main point: adding constructs to the language without a concern for keeping them all orthogonal to each other, only serves to muddle our ability to communicate unambiguously about specific things. I'm not arguing against the concept of namespacing. I do think that figuring out ways to achieve new things using the existing means, is always preferable to inventing new, subtly different, ways to achieve what is already doable. For me, a Objects are already public namespaces, closures are already private namespaces, and you can combine those to achieve whatever architecture maps best to your problem domain. I do quite like JavaScript for that sort of simplicity. All a type-level OTOH, a TIL: TS also has |
Note that namespaces are not erased, but compiled into IIFEs that create objects. A tool that supports namespace both (1) erases types and (2) adds new JavaScript code into the output. The best is when only type erasure happens, and the code you see is the code you get. If type-annotations proposal lands, and you rely on a tool to support namespace, your code will not work without a build step! It will not be portable, and it will definitely be forked! Here are namespaces in plain JS without type annotations for sake of brevity, and if we added type annotations to them, they would all work without a build step (highly portable, copy/paste in any project and it works!): // namespaces.js
export const Foo = {
n: 123,
log() { console.log(this.n) }
}
Foo.n += 123
export const Bar = new (class Bar {
n = 123
log() { console.log(this.n) }
constructor() { this.n += 123 }
})
export class Baz {
static n = 123
static log() { console.log(this.n) }
static { this.n += 123 }
} // another.js
export let n = 123
export const log = () => console.log(n)
n += 123 import {Foo, Bar, Baz} from './namespaces.js'
import * as Boz from './another.js'
Foo.log()
Bar.log()
Baz.log()
Boz.log() |
@egasimus I can see your concern and approach. if the proposal flavors #188 and the content inside the Although IMO setting additional guideline and syntax within the As for // foo.ts
export function foo() {}
export namespace foo {
export type Options = {}
export namespace boo {
export type X = {}
}
}
// usage.ts
import { foo } from './foo.js'
foo()
const x: foo.Options = {}
const y: foo.boo.X = {} The reason is that
@trusktr yes. That is the specific implementation in TypeScript, which is strongly discouraged by the TypeScript team nowadays as we moved to modules. It was a legacy behavior in TypeScript 1.x where it was still the wild wide west. namespace MyCompany {
namespace MyProduct {
const function foo() { ... }
}
}
MyCompany.MyProduct.foo() Nowadays Thus it is type specific and can be safely erased. |
Another key use case for export type Foo<T> = SomePreconditionCheck<T> extends true ? Foo.Impl<T> : never
export namespace Foo {
export type Impl<T> = ...
} Since all types need to be public, doing |
This already works in TS with type erasure: // foo.ts
export function foo() {}
export interface foo {
Options: SomeType
boo: {
X: OtherType
}
} // usage.ts
import { foo } from './foo.js'
foo()
const x: foo.Options = {} // the same
const y: foo.boo.X = {} // the same |
I hadn't tested it, actually usage needs to be: const x: foo['Options'] = {}
const y: foo['boo']['X'] = {} But it works! Here's a playground. |
Yes. It "works". Quite a hack thou. 😛 Now try this 🤣 // foo.ts
export function foo() {}
export namespace foo {
export type Options = {}
export namespace boo {
export type X = {}
}
}
export interface foo {
name: string
value: string
}
// usage.ts
import { foo } from './foo.js'
foo()
const x: foo.Options = {n: 123}
const y: foo.boo.X = {s: 'asdf'}
const f: foo = {name: 'x', value: 'y'} https://playground.solidjs.com/anonymous/1233e8c1-14f1-456b-9980-7e8c250a4299 |
That one gets confusing, one would think https://playground.solidjs.com/anonymous/cddf50e9-0050-4716-b904-a856451dcb54 (guilty pleasure: using Solid.js playground as a TS playground 😄) |
The bottom line of these is that, the It is a common use case thou, as in #167 (comment) |
I think you can just avoid |
Not really. At least not in TypeScript. If not you will run into that nasty "the type cannot be named" error. |
|
Yes. That's because EDIT: I should clarify that this referring to the typical use cases. Technically, a file is considered a script file or module file based on whether it contains top-level |
Further more, when a |
Yes. To make it clear, script.d.ts // this is a script file
declare type S = {} module.d.ts // this is a module file
export type Foo = {} usage.ts /// <reference path="script.d.ts" />
// ☝️ this is how to use the script file: triple-slash references
const s: S = {}
import { Foo } from './module.d.ts'
// ☝️ importing from a module d.ts file.
// It works, but generally it's better to name it as `module.ts` instead
const f: Foo Another way to include a script file is adding them from tsconfig using |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Just sharing an example on how do I use export type SelectWithDistribute<
T,
U,
$O extends SelectWithDistribute.$Options = {}
> = IsAnyOrNever<
T,
$SelectionBranch
> extends infer R
? R extends $Then ? $ResolveSelection<$O, T, $Else>
: R extends $Else ? (
$ResolveOptions<[$O['distributive'], SelectWithDistribute.$Default['distributive']]> extends true
? SelectWithDistribute._D<T, U, $O>
: SelectWithDistribute._N<T, U, $O>
)
: never : never
export namespace SelectWithDistribute {
export type $Options = $SelectionOptions & $DistributiveOptions
export type $Default = $SelectionPredicate & $DistributiveDefault
export type $Branch = $SelectionBranch & $DistributiveDefault
export type _D<T, U, $O extends SelectWithDistribute.$Options> =
T extends U ? $ResolveSelection<$O, T, $Then> : $ResolveSelection<$O, T, $Else>
export type _N<T, U, $O extends SelectWithDistribute.$Options> =
[T] extends [U] ? $ResolveSelection<$O, T, $Then> : $ResolveSelection<$O, T, $Else>
} |
Looking at that example, maybe people should consider TS syntax isn't a goal to achieve, but a lession of what not to repeat. It's almost as verbose as SQL... How many times does one need to repeat |
Generally agreed. I'm also not advocating to do it the TypeScript way. But I think this is off-topic on this thread. |
namespace
is a good way to organize types.It may worth considering adding type-only namespace to the spec.
In value-level, you can organize values and functions into an object literal:
namespace
allows you to do the same with types:This avoid export pollution so that consumer is not overwhelmed by all the extra types a package exports, when those types are typically not used directly.
The text was updated successfully, but these errors were encountered: