tc39 / proposal-type-annotations

ECMAScript proposal for type syntax that is erased - Stage 1
https://tc39.es/proposal-type-annotations/
4.22k stars 46 forks source link

consider namespace #167

Open unional opened 1 year ago

unional commented 1 year ago

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:

export const group = {
  func1,
  value2
}

namespace allows you to do the same with types:

export namespace foo {
  export type Param = { ... }
  export type ReturnType = { ... }
}

export function foo(param: foo.Param): foo.ReturnType { ... }

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.

trusktr commented 11 months ago

I think this is covered by the ideas in

unional commented 11 months ago

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
egasimus commented 11 months ago

What of this can't be achieved by putting the code in separate files?

export pollution so that consumer is not overwhelmed by all the extra types a package exports

This sounds like a UI problem :confused:

unional commented 11 months ago

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 .h and .c file in C, but supporting co-location of type and code and provide a way to neatly organize them is very beneficial IMO.

unional commented 11 months ago

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'
egasimus commented 11 months ago

Another reason for namespace is that flatten list (direct export) is bound to conflict:

I don't understand this example. Isn't that what import * as Foo from './foo.js' is for?

egasimus commented 11 months ago

supporting co-location of type and code and provide a way to neatly organize them is very beneficial IMO.

Agreed.

egasimus commented 11 months ago

Speaking of which, https://github.com/tc39/proposal-type-annotations/issues/188 approach may make this a bit more cumbersome:

I would say more than a little.

egasimus commented 11 months ago

@unional

Yes, I would say it is a DX problem.

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 namespaces: a new layer of nesting that doesn't correspond to the filesystem layout of the source code.

For the sake of Occam's razor, my question stands:

What of this can't be achieved by putting the code in separate files?

And, conversely, in what context would you export so many definitions from a single file that you need namespaces to organize them?

unional commented 11 months ago

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 ts-toolbelt uses import * as A from './Any/_api to achieve the same thing.

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 import * as X) is a nice way to reduce the clutter.

This is a general issue of modules. When we move from global namespace (window.MyCompany.MyPackage.foo) to module (AMD, CommonJS, ESM), we are effectively "flatten" the exports to a single level. When the package is small, it works great. But when the package is large, you will start missing the organization benefits of namespaces.

And really, namespaces is just "type literals" as compare to "object literals" (export const x = { a, b, c }) for organization purposes.

trusktr commented 11 months ago

I'm not sure if it is covered there. Do you mean:

// ...
import: { type A } from './a.js'
// ...

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;
trusktr commented 11 months ago
// foo.js
export: {
  export namespace foo {
    export type Options { ... }
  }
}

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 { ... }
  }
}
// implicit import of types?
const o: foo.Options = {}

This has nothing to do with the comment syntax, this is up to the type system.

// or koo.js
import: { type foo } from './foo.js'
import { foo } from './foo.js'

This would be:

// or koo.js
: import { type foo } from './foo.js'
import { foo } from './foo.js'

but because : already makes it a type feature, TypeScript can adapt to not require type in that case. Like this:

// 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.)

trusktr commented 11 months ago

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'

This doesn't relate to the syntax. TS can do whatever it wants. #188 is only specifying the comment space.

I don't understand this example. Isn't that what import * as Foo from './foo.js' is for?

Indeed, this is preferred over "deprecated" namespace. I never use namespace, and I have no conflicts.

the language should not add something as elaborate as namespaces

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 :, might need to be double ::, f.e.

:: rm -rf /
import { foo } from './foo.js'

Paste any TypeScript code here, and I'll show the rough equivalent with type comments from 188.

msadeqhe commented 11 months ago

Not sure if there's a limitation with :, might need to be double ::,

@trusktr, Yes, there's a limitation with :, because it ends with ,, ;, = or {. ::-style comment is a good idea to have instead of :{comment}. Thanks.

egasimus commented 11 months ago

@unional

This is a general issue of modules. When we move from global namespace (window.MyCompany.MyPackage.foo) to module (AMD, CommonJS, ESM), we are effectively "flatten" the exports to a single level. When the package is small, it works great. But when the package is large, you will start missing the organization benefits of namespaces.

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 namespace keyword.

export so many definitions from a single file

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 ts-toolbelt was written as a single file".

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 namespace keyword falls in the latter category: it doesn't really map to any concept that is not already trivially representable.

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 namespace construct could add on top of it would be more assumptions to wrap one's head around (and to work around, if they turn out to be wrong in a given case.)

OTOH, a namespace/module keyword that actually creates and populates an ES Module scope (an existing concept) without the need to create a separate source file, is definitely an idea worth exploring - but it's also quite separate from the concerns of this proposal.

TIL: TS also has module which is... somehow similar to namespace but also different? And then there's also package. Having all three is a bit of a red flag honestly. I haven't looked into the details of what makes them distinct, given that they're erased at runtime in favor of what the file system, module/package system, and bundler do.

trusktr commented 11 months ago

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()
unional commented 11 months ago

@egasimus I can see your concern and approach. For me, I focus on DX. Maybe a little too much and not the direct concern to the proposal.

if the proposal flavors #188 and the content inside the :comment is vendor specific, this could be part of that.

Although IMO setting additional guideline and syntax within the :comment block would still be beneficial, or else the tools will diverge and it could become another battleground for "browser war" or "TypeScript vs Flow".

As for import * as X as a mechanism to achieve the same thing as namespace, it works for one specific scenario, but not a general replacement. For example:

// 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 import * as X creates a namespace module (or module namespace, something like that). It is a specific construct.

Note that namespaces are not erased, but compiled into IIFEs that create objects.

@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. It was used to mimic the global namespace behavior:

namespace MyCompany {
  namespace MyProduct {
    const function foo() { ... }
  }
}

MyCompany.MyProduct.foo()

Nowadays namespace is primarily used for organizing types, which is what I propose here.

Thus it is type specific and can be safely erased.

unional commented 11 months ago

Another key use case for namespace: hiding implementation details

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 Foo.Impl allows those internal utility types to be hidden, or "tuck" under the public type.

trusktr commented 11 months ago
// 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 = {}

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
trusktr commented 11 months ago

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.

unional commented 11 months ago

I hadn't tested it, actually usage needs to be:

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

trusktr commented 11 months ago

That one gets confusing, one would think f should be callable. Probably a deficiency of mixing both. We can just use this (assuming f should not be callable) and it is clear:

https://playground.solidjs.com/anonymous/cddf50e9-0050-4716-b904-a856451dcb54

(guilty pleasure: using Solid.js playground as a TS playground 😄)

unional commented 11 months ago

f is not callable. const c: typeof foo is. 😆

The bottom line of these is that, the namespace "object" is the same concept of the "namespace module" as in import * as Y, which is a different construct. It is not an object nor callable. It is just a bag to hold additional stuff (in our case here, just types).

It is a common use case thou, as in https://github.com/tc39/proposal-type-annotations/issues/167#issuecomment-1731059591

trusktr commented 11 months ago

I think you can just avoid exporting a type to keep it private, right?

unional commented 11 months ago

I think you can just avoid exporting a type to keep it private, right?

Not really. At least not in TypeScript.

If not you will run into that nasty "the type cannot be named" error.

lillallol commented 11 months ago

Not really. At least not in TypeScript.

~~There is no local scope in .d.ts files, i.e. whatever you define in a .d.ts file, regardless of being exported or not, is available on all other .d.ts files. For .ts which export values or types, there is local scope for types (and values), i.e. what I described for .d.ts is not valid for .ts. link~~

unional commented 11 months ago

There is no local scope in .d.ts files

Yes. That's because .d.ts files are script files, not module files.

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 import or export, regardless of file extensions (as pointed out by @zaygraveyard below).

zaygraveyard commented 11 months ago

Further more, when a .d.ts includes an import or export statement it becomes a module file and thus nothing will be exported by default.

unional commented 11 months ago

Further more, when a .d.ts includes an import or export statement it becomes a module file and thus nothing will be exported by default.

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 files or include.

egasimus commented 11 months ago

Learning so many things about TypeScript from this thread, none of which I would possibly have been able to infer from the "first principles" of how JavaScript works! Fascinating!

While I've seen how, when pressed, the TypeScript team or advanced TypeScript users can provide details and rationale on why each individual thing is like that, tbh the whole of it looks like a classic Microsoft kludge, and the overall reason is "so that they can give you their IDE to help you navigate it" - or, when it all becomes so overengineered that your intent becomes inexpressible in point-and-click terms, a whole Copilot (@somebody1234, here's where the "AI suggestions" fit into the picture, like you'd need that explained...)

Call me old-fashioned, but apparently I don't even know what DX is! for me DX means providing developers downstream from you with things that conform to DRY, KISS, YAGNI, principle of least surprise, Unix, etc., so they would not have a terrible experience actually wrapping their head around them. Even the most awesome language-specific support in your editor can only make tolerable the kind of development experience where you don't know what you don't know and learn about it at the most inopportune times.

somebody1234 commented 11 months ago

@egasimus my point though, is that copilot is completely unrelated to typescript though. typescript is not part of some secret agenda to force us to use copilot, no more than c# or f# or powershell are. nor do i think copilot is somehow especially more useful for microsoft's languages over everyone else's. plus if you're using simple interfaces, typescript isn't more complex than any other language; if you're using complex conditional types then LLMs often get the logic incorrect (depending on complexity of course. and whether someone's written such a type before)

unional commented 11 months ago

if you're using complex conditional types then LLMs often get the logic incorrect

Definitely. And LLM generally can't produce well structured/well architected code, because well structured/well architected code are a tiny percentage compare to all other code used to train the model.

somebody1234 commented 11 months ago

as for why typescript DX is relatively bad - i think the root cause is that typescript is sorely lacking in good documentation, likely due to a shortage of manpower. the typescript handbook is often sorely lacking in documentation for newer features, and while it does contain beginner's guides, i do feel like they're kinda a high level overview, meaning at some point you do have to read the entire handbook, which as mentioned before is a bit lacking. one other issue is that i don't think there are any guides on how to actually design types (be it simple or complex ones) for real world usecases

unional commented 11 months ago

as for why typescript DX is relatively bad - i think the root cause is that typescript is sorely lacking in good documentation, likely due to a shortage of manpower. the typescript handbook is often sorely lacking in documentation for newer features, and while it does contain beginner's guides, i do feel like they're kinda a high level overview, meaning at some point you do have to read the entire handbook, which as mentioned before is a bit lacking. one other issue is that i don't think there are any guides on how to actually design types (be it simple or complex ones) for real world usecases

IMHO, I think the problem lies in its design goal. i.e. the soundness issue. It is a necessary evil, given how TypeScript rapidly grow and serve the community. There are just many cracks and hacks in the type system that the general engineer doesn't know (yes, that part is documentation related, per se), or simply "not possible"/"working as designed"/"design limitation" that you can't even reason about.

I'm not laying blame on the TypeScript team, but rather thankful that they have created TypeScript that "works reasonably well" in many cases.

It's just as a library author and sort-of advance user of TypeScript, I have to deal with those hard cases almost daily.

At work, I told my junior engineers who are learning the rope:

"TypeScript is a great tool and is very useful. But doing those type-level stuff can get complicated REALLY fast. When you work on the code and create the types, 70% of the time you won't have any problem. But if you do, and you can't figure it out within 15 minutes, ping me and don't try to work it out yourself."

egasimus commented 11 months ago

not part of some secret agenda to force us to use copilot

Citation needed.

Remember Microsoft trying to sue out of existence Linux and the nascent global FOSS ecosystem centered around it, back in the 90s? I'm sure since then they've thrown away all their organizational culture and rebuilt around a completely antithetical development model. I'll believe that when I see Windows under the GPL :shrug:

Not to put too fine a tinfoil hat on it, but with the huge changes that the Internet has been causing in society, I totally wouldn't be surprised if in some quarters it is believed that there are too many computer-empowered people already, and the whole thing ultimately serves as a subtle gatekeeping effort. Sorta like pay to play, only since nobody pays for dev tools with money, they get to pay in terms of time wasted and frustration experienced. It's like in that Tarkovsky movie - you get through the wringer intact, you're good to go...

i think the root cause is that typescript is sorely lacking in good documentation

IMHO, I think the problem lies in its design goal

For me the problem is in the execution. It's not a bad design goal so much as numerous bad design decisions. The lack of good documentation? That's downstream from the whole thing not making a terrible lot of sense in the first place. Lack of a formal specification is also fun - latest "spec" I found was v1.8 from 2016, and, well, more of a manual really.

But hey, who needs the behavior of the language to be comprehensible to the developer? Here's VSCode and Copilot to comprehend it in your stead! That's, like, infinitely better! No, you don't have to use them, what are you saying? You just have to deal with all the devs who do - and, as a result, are perfectly comfortable remaining oblivious of what things work for everyone - in favor of what works often enough for most people...

To paraphrase that old traders' saying: a corporation of that caliber can afford to keep its bad ideas afloat for longer than anyone can stay rational. Is "things that make money have a logic to them" now a conspiracy theory? Because it doesn't take a shadowy cabal to take a good thing and extract value from it until it's ruined. All it takes is ignorance and indifference - which are what software giants traditionally thrive on, as software remains a zero marginal cost good, and the name of the game has always been simply mindshare.

if you [...] can't figure it out within 15 minutes [...] don't try to work it out yourself.

Reasonable. Still sad though. Kudos if you manage to foster an environment conductive to learning and attaining fluency, while trying to deliver software on top of what's effectively a complexity minefield.

If it wasn't for Web programming making it possible for one to work things out for oneself without ending up in the tarpit (most of the time), a lot of us wouldn't be where we are now. Microsoft is awesome at building things that interpose themselves between you and how things actually work under the hood. (Solution files, anyone?) Which is why I find their entire participation in the open source ecosystem eminently problematic, sort of a Trojan horse really.

one other issue is that i don't think there are any guides on how to actually design types (be it simple or complex ones) for real world usecases

That also touches on the larger issue of "how does one learn programming" (and how to prevent them from doing so, heh). After all, nobody can anticipate your specific use case - if they did, they'd just write a library for it (another previously trivial thing made worse by TypeScript, nuff said - should be obvious what behaviors this incentivizes and disincentivizes.)

IMHO the best answer we have is "you learn programming by doing programming" - and when things that would seemingly make sense blow up in your face for reasons somewhere between arbitrary and esoteric, learning by doing is effectively precluded.

It's not like languages that have a working native type system have special guides for how to design types, either. Such may even be counterproductive, encouraging a copy/paste approach to system architecture. I'm not even sure what those would entail for a language where the type system actually grows logically out of the rest of the language. Learn what each "piece" is for, and how they fit together - then use them to work on the actual problem you're solving, not to paper over deficiencies of the language. If you have to do the latter too much... it's just a bad language :shrug:

I'd expect fluency with the type system to grow logically out of comprehending the rest of the language, and in my experience, TypeScript's "bolt-on" nature betrayed that expectation quite dramatically. My thinking here is any type system designed to be a truly integral part of JavaScript would have no other option but to do better, for reasons entirely technical and not organizational or political.

P.S.

I'm not laying blame on the TypeScript team

I am. They know. I count that as a win for all that is good. Nobody expected them to anticipate all consequences of their work, conversely they can't expect to stay ignorant of them forever. Out of who knows how many developers being incessantly tripped up by their brainchild, someone was bound to get pissed and tell them how it is. So I went and did just that - and their reaction was quite predictable: they did the equivalent of wadding up some cash and plugging their ears with it :rofl:

ljharb commented 11 months ago

Let's please avoid conspiracy theories and attacking specific people or corporations, or questioning their motives. I'll remind you that this behavior violates our Code of Conduct.

egasimus commented 11 months ago

TIL: It's a conspiracy theory that things are bad. Says it right here.

In other fake news, an individual is in an asymmetrical relationship to the corporation manufacturing the tools of his trade. More at 11.

I question nobody's motives - businesses exist to make money. It's perfectly rational for the different devtools in Microsoft's product portfolio to synergize. Yay, Microsoft! I just don't think the result is any good. Boo, me.

I do acknowledge my apparent difficulty not to divert the conversation towards the less fine points of TypeScript - considering TS is the reference implementation for most of the concepts we discuss here and a large part of the reason why the matter of adding native type annotation support to JS is under debate, I believe those remain quite pertinent.

May all who feel disturbed or offended by my wild speculations please accept my apologies for all prior, current, and future instances of such aspects of my behavior as they would deem to find problematic. I mean a lot of things but no harm - and am grateful to be learning quite a lot from everybody here despite continued attempts to suppress my speech rather than to respond to it with things that I don't know.

azder commented 11 months ago

@ljharb I don't see a conspiracy theory. As for attacking specific people or corporations, yes the above comments might be interpreted as such. I think @egasimus wasn't trying to attack, but to explain a certain point of view even if it might have ended up like an attack once read by others.

So, let's talk about generic corporations.

Let's say you're heading a big part of a much bigger corporation and you're trying to make money. Now, sellin or giving oridnary programmers stuff doesn't quite make you money, but if you target other businesses, large ones even, then you can charge for solving their issues.

The above isn't a conspiracy, it's a valid market strategy.

Now, you have customers coming to you and saying they can't quite reach their goals of hiring enough programmers, let alone have the time to properly train them or weed out those that just can't figure out the finer points, pass some basics, of the one language of the Web that everyone is forced to use.

You might want to give them tools to complete that task, but JS (or EcmaScript) is messy enough by nature that writing tools for it is hard. Tools that inference that dynamic language will make for a time consumming effort to make a good enouogh parser and other issues of the sort. But, if you just add another language on top of it, like TypeScript or Flow or whichever next may come, then you'd be able to make a less complicated parser for less time and place it on the market.

The above is all a valid strategy. No conspiracy. I might be doing the same in that position.

Alas, I'm on the different end of the spectrum - a single programmer that can manage quite OK with using the language meant for amateurs (hoobysts, lovers from "amor"). I don't want to be forced into more complicated code simply because my goals and use cases are different from a big corporation. I'm fine without using some tools since I can provide relatively safe checks and good test coverage that I'd prefer to solve customer's issues, not spend the day resolving static typing compiler errors. And it would be hindrance to me if I have to train myself to ignore large parts of the code simply because others might need them there (e.g. a tag soup after : in function parameters). I'd prefer if it's besides, not in between the code I'm interested in.

There should be a place for both cases without one stepping on the toes of the other.

And wether that was TS in the past and may be some "Scriptchecker" yet to be invented, or even providing a SQL for checking some JS objects that are meant to represent database models, all of those should be able to co-exist on top of JS, not as part of it. All that JS needs to do is to provide a level playing field so that any of those extra tools (DSLs, compilers, bundlers etc.) don't limit or exclude others (tools or people).

I think, it can be accomplished without changing syntax of EcmaScript, at least as a way to test drive how it might work by using import-from-assert and code comments (inside //, maybe not /**/) that the language will ignore, but some tools will not.

unional commented 11 months ago

Just sharing an example on how do I use namespace:

https://github.com/unional/type-plus/blob/main/type-plus/ts/type_plus/branch/select_with_distribute.ts

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>
}
azder commented 11 months ago

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 export namespace and export type?

unional commented 11 months ago

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 export namespace and export type?

Generally agreed. I'm also not advocating to do it the TypeScript way. It has a lot of technical debts making type-level programming quite hard.

But I think this is off-topic on this thread.