tc39 / proposal-type-annotations

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

Type annotations won't unfork JavaScript #171

Open eemeli opened 1 year ago

eemeli commented 1 year ago

This proposal starts from an admirable motivation of seeking to "Unfork JavaScript", and presents type annotations as a solution to this. Specifically:

This proposal formalizes an ergonomic syntax space for comments, to integrate the needs of type-checked forks of JavaScript.

Further on, another aspect of the proposal is presented as a side benefit, but not as a motivation for the proposal:

This proposal will reduce the need to have a build step which can make some development set-ups much simpler. Users can simply run the code they wrote.

While I can see how type annotations may "reduce the need to have a build step", I am not at all convinced by the claim that the proposal will unfork JavaScript. Let us consider the forks that the proposal itself addresses, i.e. TypeScript and Flow. If the type annotations proposal were to be accepted, we would immediately end up with at least the following JS variants:

In other words, we've started out by going from three to five forks. The proposal really should explain what the next steps from here might be: How do these variants merge into a smaller number than what we currently have? Are Flow and TS as languages expected to make backwards-incompatible updates to reduce their footprint to what is allowed in this proposal? If so, that's an extraordinary claim that requires extraordinary proof which is completely missing here.

The recently-added TS satisfies operator is a good example of active work and innovation that's happening in this space, but it's not supported by this proposal. Going forward, what is the expectation for how innovation in this space should proceed when it seeks to step outside the defined bounds of :, ?, !, <...> and as to initiate its syntax? I would argue that satisfies shows very practically that there's current interest in such development, and that the likely development direction of a language like TS isn't contraction but expansion.

Furthermore, even if we were to have TS and Flow contract to the bounds defined by this proposal, we would still be left with at least three variants of JavaScript. Sure, they could now all be more clearly defined as "JavaScript" and parsed by a single processor, but the developer experience of working with them will be left as similar as possible to the current state of affairs. They will not have been "unforked", unless that word means something technically different from how it's commonly understood.

Type annotations are not a viable solution for the stated motivation of this proposal. Either the proposed solution or its motivation needs to change.

orta commented 1 year ago

I'm not an author, but from my perspective, at language/runtime level, there would only be JavaScript files regardless of the variant of type systems inside it. Python and Ruby has many different type linters and I've never heard of people considering those as forks for the language. It would be similar to saying that you could have a version of javascript which uses // comments or /** comments.

Given TypeScript's general backwards compatibility goals, I think it's safe to assume that .ts would never go away, (and probably flow's) and that the .ts files would be where the team has full control over how they apply syntax.

Then, if they want to re-use those same concepts inside JS with this proposal, it's on that team to find a way to make those ideas fit in the syntax space specified. satisfies is a good case, as is as const but it's not difficult to imagine that they can be re-used as specially named generics, via comments like JSDoc support for JS via const foo = /** @type {const} */ ({x: 1}); or something more native like const foo: (satisfies XYZ) = {}


Then WRT the "unforking", I think it's reasonable to imagine that once people can start using type annotations in JavaScript files, then they will move to .js files in order to align with the language and its general direction.

Switching would enforce removal of expression-level changes in languages available in TypeScript files. .ts files will still exist, but it's likely to be a niche for first getting features, or for backwards compatibility.

Flow may even be able to fully fit within the constraints of this proposal, given they mainly focus on a few codebases, have few expression features and can make more sweeping changes regarding backwards comparability.

I think if folks are migrating from .ts to a .js file which fully conforms to the ECMAScript spec then you are migrating to a JavaScript codebase regardless of whether you then run the TS compiler over the .js files. Which brings people back from the TypeScript fork of JavaScript, which makes it reasonable to describe as "un-forking" the language.

ReinsBrain commented 1 year ago

Hi @eemeli , you wrote:

In other words, we've started out by going from three to five forks.

That is probably true. However, with type annotations available, I think over time new projects that might have been chosen as typescript or flow will opt for native typing semantics. Long-lived projects, when their time comes due for refactor|upgrade, may also see transition. Over the same span, I'd hope type annotations will expand to have similar richness of expressiveness that typescript and flow offer today.

Like anything in ecmascript, change spans across a longer horizon. But, the trajectory does appear that it will eventually make the forks moot. It won't be overnight, but then, no change in javascript ever is.

trusktr commented 1 year ago

One of TypeScript's more recent goals is staying aligned (and working with) TC39 EcmaScript spec authors. I place money on my bet that if a standard type comment syntax lands, many many projects will want to migrate (I instantly would), and new type checkers would probably spring up, and TypeScript itself (aligning with standards) will put a strong focus on embracing the new syntax.

At the end of the day, if we can write libraries with standard type comments, and anyone can fork them and run them in their browser or other JS engines (without running the any library's specific type checker of choice). That's really great: it makes libraries highly accessible without build tools, avoiding issues in edge-cases depending on their OS. Even if someone doesn't have type checking in some library because it doesn't use their type checker of choice, they can at least move forward and run the code, staying true to at least the premise of plain JavaScript. From my perspective, I think that that's what "unforked JavaScript" is about.

trusktr commented 1 year ago

(Adding onto my previous comment) Imagine: without having to run any build steps, without having to produce any build output, you write some JavaScript code (with your desired type checker syntax, if you wish), and you ship it, and then it can run in any JavaScript engine, can be imported by any other JavaScript code, with no special requirements. I believe that's part of the vision.

Here's a view of "forked JavaScript":

right now, plain JS users who don't use build tools (beginners, or anyone who likes to keep things simple, maybe they build experiential experiences that don't need optimization for gazillion users, etc), they cannot copy/paste TypeScript code from somewhere and expect it to work, because it is forked into an incompatible format. This proposal unforks that.

I think that forked type checkers and type syntax is distinct from forked JavaScript: this unforks JavaScript, but does not unfork types/TypeScript/Flow.

trusktr commented 1 year ago

The recently-added TS satisfies operator is a good example of active work and innovation that's happening in this space, but it's not supported by this proposal

Based on #188, satisfies can be implemented by a type checker using this syntax:

const foo = {
  // ...
}: satisfies SomeType

where the : satisfies SomeType is a trailing type comment, similar to having written

const foo = {
  // ...
} // satisfies SomeType
trusktr commented 1 year ago

@eemeli curious to see what deficiencies you see in #188 that will not allow almost all of TypeScript's and Flow's features to be implemented.

lillallol commented 1 year ago

Imagine: without having to run any build steps, without having to produce any build output, you write some JavaScript code (with your desired type checker syntax, if you wish), and you ship it, and then it can run in any JavaScript engine, can be imported by any other JavaScript code, with no special requirements.

You can already get full TS static type checking, using nothing more than already native TS, without the need to compile. The argument about the context proposal reducing a build step is laughable.

egasimus commented 1 year ago

You can already get full TS static type checking, using nothing more than already native TS, without the need to compile

Having a compile step in a scripting language is what's laughable.

trusktr commented 1 year ago

You can already get full TS static type checking, using nothing more than already native TS, without the need to compile. The argument about the context proposal reducing a build step is laughable.

Having a compile step in a scripting language is what's laughable.

Let's all please show examples to help people understand our points of view. Btw, saying that things are laughable doesn't help, some people may find offense.

egasimus commented 1 year ago

I do wish I knew how to concoct technical examples that would cause people already deeply committed to a given point of view to reconsider it - rather than ignore the example :laughing: Good thing multiple points of view can coexist.

I think @lillallol talks sense and I agree with some of their conclusions. That said, I do find their usage of "laughable", well, laughable, and I find the following sentence quite upsetting in its own right:

already get full TS static type checking, using nothing more than already native TS, without the need to compile.

@lillallol @trusktr Would it be beneficial if I actualy elaborated on the things I find wrong with this sentence?

eemeli commented 1 year ago

@eemeli curious to see what deficiencies you see in #188 that will not allow almost all of TypeScript's and Flow's features to be implemented.

Unfortunately, my sense is that this isn't a direction supported by the champions of this proposal. I tried asking something along these lines during the community call, and @littledan's reply (apologies, paraphrasing here from memory) was that not initially including a grammar that's sufficiently powerful to allow most current TS to be parsed with it would lead to further unwanted fracturing of the typed JS ecosystem.

So where in the original post here I talk of five forks, we could end up with many more, as wholly new typing languages would be developed to fit within the smaller slot that's made available. Which is actually starting to sound pretty interesting to me, as building a typing system can be quite a bit of work, both technically as well as socially. Making that easier (to me, a core part of this proposal) could lead to greater innovation and development in this space.

ctcpip commented 1 year ago

Gentle reminder that all participants are expected to abide by the TC39 Code of Conduct. In summary: