A Proposal For Type Syntax in JavaScript

Daniel Rosenwasser

Today we’re excited to announce our support and collaboration on a new Stage 0 proposal to bring optional and erasable type syntax to JavaScript. Because this new syntax wouldn’t change how surrounding code runs, it would effectively act as comments. We think this has the potential to make TypeScript easier and faster to use for development at every scale. We’d like to talk about why we’re pursuing this, and how this proposal works at a high level.

Background

One recent trend our team has seen in the JavaScript world is a demand for faster iteration time and a reduction of build steps. In other words, "make it faster and make it simpler".

In some ways, this is already happening. Thanks to the success of evergreen browsers, developers can often avoid compiling newer versions of JavaScript to run on older runtimes. To some extent the same is also true of bundling – most browsers have built-in support for using modules, so bundling can be viewed as more of an optimization step than a necessity. This has increasingly been the case, so how is TypeScript keeping up?

If we go back to 2012 when TypeScript was first announced, the JavaScript world was drastically different! Some browsers shipped often, but not all. It was unclear how long we’d be stuck with ancient versions of Internet Explorer, and that led to tools like bundlers and compilers gaining adoption. TypeScript was able to really thrive in the age where adding a build step to JavaScript was a given – after all, if you need to compile your JavaScript anyway, why not compile away your types too? But if those trends we mentioned above continue, compiling away your types might be the only step between writing your TypeScript and running it, and we don’t want to be the ones standing in the way of a good developer experience!

In some ways, our JavaScript support bridges the gap here, and maybe you’ve seen this if you use an editor like Visual Studio or Visual Studio Code. Today, you can create a .js file in your editor and start sprinkling in types in the form of JSDoc comments.

/**
 * @param a {number}
 * @param b {number}
 */
function add(a, b) {
    return a + b;
}

Because these are just comments, they don’t change how your code runs at all – they’re just a form of documentation, but TypeScript uses them to give you a better JavaScript editing experience through things like code completions, refactorings, and more. You can even add type-checking by adding a // @ts-check comment to the top of your file, or running those files through the TypeScript compiler with checkJs. This feature makes it incredibly convenient to get some of the TypeScript experience without a build step, and you can use it for small scripts, basic web pages, server code in Node.js, etc.

Still, you’ll notice that this is a little verbose – we love how lightweight the inner-loop is for writing JavaScript, but we’re missing how convenient TypeScript makes it to just write types.

So what if we had both?

What if we could have something like TypeScript syntax which was totally ignored – sort of like comments – in JavaScript.

function add(a: number, b: number) {
    return a + b;
}

Our team believes there is a lot of potential here, and this month we’re hoping to bring it forward in a proposal to TC39, the ECMAScript standards committee!

How Would This Work?

When we’ve been asked "when are types coming to JavaScript?", we’ve had to hesitate to answer. Historically, the problem was that if you asked developers what they had in mind for types in JavaScript, you’d get many different answers. Some felt that types should be totally ignored, while others felt like they should have some meaning – possibly that they should enforce some sort of runtime validation, or that they should be introspectable, or that they should act as hints to the engine for optimization, and more! But in the last few years we’ve seen people converge more towards a design that works well with the direction TypeScript has moved towards – that types are totally ignored and erasable syntax at runtime. This convergence, alongside the broad use of TypeScript, made us feel more confident when several JavaScript and TypeScript developers outside of our core team approached us once more about a proposal called "types as comments".

The idea of this proposal is that JavaScript could carve out a set of syntax for types that engines would entirely ignore, but which tools like TypeScript, Flow, and others could use. This allows us to keep the things you love about TypeScript – its type-checking and editing experience – while removing the need for a build step in development.

So when it comes to writing and running code, a developer’s inner-loop would look a little different.

Today TypeScript goes through a process of compiling from .ts files to .js files, which then run in the browser. Our proposal is to simply have browsers run the input JavaScript code which would support type annotations.

Meanwhile, writing code and type-checking would stay the same. A developer could get instant type-checking feedback in an editor with TypeScript support, run TypeScript on the command line, and add TypeScript as part of their CI tasks. The biggest difference is that because we would not need a build step, we would dramatically lower the barrier to entry for JavaScript devs to experience the power of types and great tooling.

TypeScript and the VS family of editors can provide errors/diagnostics for both TypeScript and JavaScript files that use type annotations. This process is roughly what the tools do today.

To make this happen, JavaScript would minimally need to add syntax for things like type annotations on variables and functions, optionality modifiers (?) for parameters and class members, type declarations (interfaces and type aliases), and type assertion operators (as and !) – all of which would have no effect on how the surrounding code is run.

Things like visibility modifiers (e.g. public, private, and protected) might be in scope as well; however, enums, namespaces, and parameter properties would be out of scope for this proposal since they have observable runtime behavior. Those features could be proposed as separate ECMAScript features based on feedback, but our current goal is to support some large subset of TypeScript that we think could be a valuable addition to JavaScript.

With this carve out, we’ve left room for type-checkers to innovate in ways that require new syntax. That does mean that engines would happily run code with nonsensical types, but we believe type-checkers could (and should) be prescriptive and enforce stricter constraints than runtimes. Combined, this makes for a type syntax that could be customized across different checkers, or removable entirely if someone decides they’re not happy with TypeScript or any other type-checker.

What is this not?

It’s worth mentioning what this proposal isn’t.

Our team isn’t proposing putting TypeScript’s type-checking in every browser and JavaScript runtime – nor are we proposing any new type-checker to be put in the browser. We think doing that would cause problems for JavaScript and TypeScript users alike due to a range of issues, such as runtime performance, compatibility issues with existing TypeScript code, and the risk of halting innovation in the type-checking space.

Instead, we’re just proposing syntax that is compatible with and motivated by TypeScript, which could be used by any type-checker, but which would skipped over by JavaScript engines. We believe that this approach is the most promising for everyone, and would continue to allow TypeScript, Flow, and others to continue to innovate.

What’s next?

Given all this, we plan to present this proposal for Stage 1 at the upcoming March 2022 plenary meeting of TC39. We’ll be doing so with the support and guidance from our co-champions of this proposal, Rob Palmer at Bloomberg and Romulo Cintra at Igalia.

Reaching Stage 1 would mean that the standards committee believes that supporting type syntax is worth considering for ECMAScript. This isn’t a sure-fire thing – there are many valuable perspectives within the committee, and we do expect some amount of skepticism. A proposal like this will receive a lot of feedback and appropriate scrutiny. It may involve lots design changes along the way, and may take years to yield results.

But if we pull this all off, we have the chance to make one of the most impactful improvements to the world of JavaScript. We’re excited by that, and we hope you are too.

If you’re interested in hearing more about the specifics and current direction, head on over to the proposal repository. We look forward to hearing what you think!

And lastly, the TypeScript team and the champions group would like to recognize and extend our thanks to all those who worked on prior art, along with the contributors who reached out to help with types as comments, and especially Gil Tayar who helped spearhead it. We’re grateful to be part of such a passionate community!

54 comments

Discussion is closed. Login to edit/delete existing comments.

  • Manish Jain 0

    What a great news! I always wished for it. This will make debugging much easier.

  • Rishi Patel 0

    🔥🔥🔥🔥🔥

  • Athaariq Ardhiansyah 0

    No matter how hard we avoid compilation, we still need compilation for stuff like minifier and bundling

  • Juslin Komenan 0

    Can’t way !

  • Jakob Møller 0

    Why is it great that you do not have to compile code that is meant to be compiled? – imagine someone copying a snippet with types and using it with arguments of the wrong type. With type hints you could end up barking up the wrong tree until you realize that the types do not match?

    • Jakob Møller 0

      On the other hand: why not build a TypeScript compiler into the browsers – and in addition to sloppy and strict modes have a typed mode?

      • Jakob Møller 0

        Use a compiler with explicit module resolution, with no magic – use fully qualified module specifiers – like deno does?

        By the way – why did you not do that with tsc?

  • Roland doda 0

    I know that I will get a lot of hate so I ask for respect for having an opinion against it.

    First, let’s ask why Typescript because there are a lot of people who don’t like it. I, coming from languages like Java and PHP with OOP didn’t like JS in the beginning but eventually, I fall in love with it. I don’t want to expand more in my personal opinion, but Javascript without static typing is not necessarily a downside. Quite the opposite, a lot of people think that it’s a superpower but with great powers come great responsibilities and a downside is that indeed you can easily fall into some bugs.

    We can be separated because of different opinions, but we should be together in front of some facts.

    1. Everyone loves the benefits of Typescript

    It is no secret that even those who hate Typescript, love the benefits it adds like the ability to get autocompletion or catch common errors like typos or accessing a property that doesn’t exist. So it’s clear:

    > If we had a way to get the benefits of TS by just writing JS code, everyone would be happy!

    2. Typescript DX cannot be thought without IDE/editor integration

    If we had to write TS code on Notepad, that would be terrible. So the editor we use is really important for getting type IntelliSense/autocompletion and also for highlighting errors without waiting to get them in the terminal.

    In fact, nowadays even if you don’t use TS, IDEs highlight errors like typos. In my case, I use Webstorm which not only highlights typos and errors when accessing let’s say `.length` on a number variable but also provides solutions like simplifying code e.g. converting if-else to a single line with a ternary operator. For example for this code:

    function myFn(nr) {
      let res
    
      if (nr > 2) {
        return true
      } else {
        return false
      }
    
      return res
    }
    

    It says that the res variable is unused, if-else can be simplified and that the last return is unreachable code. After accepting it’s solutions, it turned the above function to:

    function myFn(nr) {
      return nr > 2
    }

    So it’s clear:

    > Nowadays IDEs help us to understand and write code. If they become smarter, the gain would be even better.

    Based on the facts above, it seems to me that if we could get IDEs to become smarter to understand our code, we can get TS benefits by just writing JS code!

    You might say that the statement above is stupid. And you might be right! But hear me out.

    Eslint reads our code and it has become really smart yelling at us for all kinds of problems, helping us write better code by suggesting things like “Enforce for loop update clause moving the counter in the right direction.” and even fixing code automatically.
    On the other hand, Github Copilot with AI not only understands our code but tries to understand what we want to write next, and it does a pretty good job there.

    So from my point of view, I don’t see Javascript missing types but I see that we miss a tool combining eslint + IDE + Copilot (AI) in a way of running our code, understanding it and providing all the benefits of TS + eslint by just writing normal Javascript.

    For me, this tool is closer compared to waiting for 5-10 years to add types in JS and not only that but doing it as a comment. Plus, if that tool exists, it will definitely add more superpowers compared to Typescript.

    ### Trying to be technical

    Don’t take this for granted, sometimes when you start working on something, better ideas and solutions come out but here is my initial thought:

    A compiler combined with a parser to enable analyzing the code dynamically. That would be integrated into IDEs and run in 2 ways:
    1: fully-code analysis (this would run only once the project is opened in IDE and saved to a cache?)
    2: code analysis only on changed files
    Together with some machine learning, training a model could provide ease in understanding the types of variables and error-prone code.

    You might ask, how about data coming from outside of the source code? Like Backend data used in the Frontend? There might be a lot of ways but here are my initial thoughts on how to solve this:

    1. In each project we can have a file e.g. `types.ide`. That file will be automatically created by IDE and it will work in this way: When IDE can’t understand the type of a variable, let’s say because it’s coming from the Backend:

    let user
    
    axios.get('/user').then(response => user = response.data)

    it will highlight and prompt the user to provide the type. A dialog can appear and the user has to only paste the response from the Backend or the IDE can provide a curl xhr request where the user can click to run it and approve the returned result. The IDE then will automatically infer the type and write it in the `types.ide` in a way that it’s better suitet for IDE to deal with it. If we want to update a type that would also be an option through GUI by simply opening that modal again and click Update.
    The `types.ide` file should be committed. Despite the local `types.ide` there must be a global one for all IDEs for global types like dom.d.ts. The existing one from TS can be used but in a way that is best suitet for IDE.

    2. Like the first one but Automatically try to understand code. In the case of backend data, since we would have a compiler that will run the code (in a smart way of course), the xhr request can run and get the types of it automatically.

    3. The approaches above open a wide range of new possibilities, at least it’s what I believe, but how can the IDE correctly infer the data coming from the Backend? I mean we could have a property that can be either a string or array depending on some cases?

    You might argue that the IDE can be smart to understand it from the way we use the data coming from the Backend. And if it sees that we use it as a string when we should be using it as an array, we can then again tell IDE through a GUI dialog that the type can be string or array. So we can always update types through GUI dialogs.

    But how if we use Typescript for that??

    For types that IDE can’t understand, we can open the GUI dialog and write Typescript to make IDE understand the external type or for any kind of type that IDE can’t understand. Of course, without changing our JS code.

    For me, all approaches look necessary. The automatic one is great. If it fails, we can use the first approach. Optionally, we can just go ahead and declare the types with TS which would save more time rather than fixing things one be one in cases where we use inferred types from IDE in a different way.

    I know that you might call me crazy (please don’t) but we want it or not, we will lead there. Typescript, IDE, eslint and AI can progress so much that:

    1. IDE will get better on Typescript, Eslint and AI integration
    2. Typescript will get better on inferring JS code and understanding types without the need to define them
    3. Eslint will get better on analyzing the code
    4. AI can be trained and get super smart understanding not only the type of a variable but much more

    So in 2 or 4 or even 7 years, I see that we will be needing less and less TS code to get TS benefits. If that’s true, it’s not about the years passing but someone starts already coding that tool that would change everything.

    So in my opinion, we should NOT add types in JS, not only as comments but no types at all. Let’s focus on what we want. And I believe, after a lot of thought, that we don’t need types in JS but smart IDE’s with AI capabilities to automatically infer types and the ability to add types in IDEs without altering the code.

    If someone wants to start such a tool, a smart idea would be to go directly with approach 3 by providing types in IDEs and storing them in a single file. It can be done like a vscode extension. Or even easier, take advantage of .d.ts files and provide a GUI option where code in JS files is mapped to types in .d.ts.

    I would love to see such a prototype of the idea. I want to do it myself but seriously I don’t have the time and can’t find it either for a while now working full-time + part-time + having a 1+ years old child.

  • Cory Parrish 0

    Just stop it!!! The panacea craze in software engineering just never ends. There is no panacea. Different languages provide different pros and cons. Languages do NOT make higher quality code, strong engineers that understand design patterns, data structures, algorithms do though. Stop Jersey-ing NYC. Leave JS alone it’s fine! Just learn how to write cleaner code and better tests!

  • leon melamud 0

    As long as it stays Backwards Compatibility, this is a great addition!

  • 佳鹏 滕 0

    Like Typing of Python but using TypeScript like type system ?

  • Frank Weindel 0

    I don’t understand why this is very exciting to anyone. All this does is further complicate the JavaScript language in the service of two popular type checking tools. We will still need those tools for any type safety guarantees. And if we need those tools anyway, how does adding this complex comment syntax that also needs to support special keywords like `type` and `interface`, as well as generics to JavaScript really help anyone?

    If the goal is for this syntax to support any type checking solution, it certainly is pigeonholing it into TypeScript and Flow. In the future if this proposal becomes standard, do IDEs default to assuming typings are TypeScript unless the `// @flow` comment is at the top of the file, or some other indicator?

    Here’s probably one of the biggest kickers for me: What if years after this proposal is adopted the developer community really starts to crave for a single typed JS standard? Suddenly all that source code we have written with flow/TS/whatever “types as comments” within .JS files becomes a burden to such a standardization. Doing so would be horribly backwards incompatible.

    Why not just propose evolving JavaScript into TypeScript itself in this proposal? Just make the types truly optional. I could get behind that way more than this. The veil of trying to maintain JavaScript as a neutral party from anyone’s preferred type checking superset with this proposal is both misleading and a waste of time.

Feedback usabilla icon