John Reilly MacGyver turned Dev 🌻❤️ TypeScript / ts-loader / fork-ts-checker-webpack-plugin / DefinitelyTyped: The Movie

Type annotations (aka, types as comments): Strong types, weakly held

5 min read 1621

Types As Comments: Strong Types, Weakly Held

Recently, a new ECMAScript proposal called type annotations (previously referred to as types as comments) was revealed. The purpose of the proposal is to allow type annotations to be valid JavaScript syntax, albeit syntax that is ignored by JavaScript engines.

The proposal is being worked on by Gil Tayar, Daniel Rosenwasser, Romulo Cintra, Rob Palmer, and others. Many of these people are from the TypeScript community — however, this proposal intentionally does not exist to benefit TypeScript alone.

It’s a contentious topic. As a regular (and longtime) TypeScript user, here’s a description of the proposal and some thoughts.

What is the proposal?

Type annotations (or “tomments,” as some have dubbed it) is a proposal that would allow for the inclusion of types in JavaScript code. Consider the following piece of TypeScript:

const theAnswer: number = 42;

At present, this is not valid JavaScript. If you try and run it in a JavaScript engine, you’ll get an error because types are not part of JavaScript syntax.

Types in the Chrome Console

Interestingly, it’s already possible to store types within JavaScript through a standard known as JSDoc. I’ve written about how TypeScript and JSDoc connect before. Essentially, the thing to note is that JSDoc allows for storing type declarations in the context of JavaScript comments.

It’s already possible to write our code sample in valid JavaScript, expressing the types within JSDoc. It looks like this:

/** @type {number} */
const theAnswer = 42;

This works but it took two lines of code instead of one. The proposal allows for types to be directly expressed rather than be written as comments. So instead of writing the JSDoc equivalent, imagine if JavaScript was happy with the following instead:

const theAnswer: number = 42;

That’s what the proposal amounts to.



What isn’t it?

Now that we understand what the proposal is, let’s consider what it isn’t.

Type annotations isn’t an endorsement of a particular type system. Furthermore, it is not type checking in the browser or type checking in Node.js.

Let’s consider each of these. There’s a number of languages that allow us to type check JavaScript: TypeScript, Flow, Hegel and others all play this role. They are similar, but have different syntax and do different things.

What they have in common is the space where types live in their syntax or grammar. The proposal essentially says “Hey, we might not have different approaches to describing types, but we agree about where the types ought to live — let’s standardize that.”

This is why the term “types as comments,” as the proposal was formerly called, is key: these types would be ignored by JavaScript runtimes. The fact that they would be ignored is an indication that no existing type system would be “anointed” by this proposal.

Consider the following:

const theAnswer: gibberish = 42;

This is neither TypeScript nor Flow; both would complain about the above. But if the type annotations proposal were adopted, JavaScript would be entirely untroubled.

To reiterate: the proposal is not an endorsement of any given type system and it follows that there is no runtime type checking being introduced to JavaScript.

Why do this at all?

It’s worth taking a look at Daniel Rosenwasser‘s post where he announces the proposal. Daniel is part of the TypeScript team and one of the champions of this proposal, along with Rob Palmer at Bloomberg and Romulo Cintra at Igalia.

Daniel says:

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 … 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;
}

What I take from this is that JavaScript with type annotations would be a more developer-friendly JSDoc.

‘It’s the JSDoc I always wanted!’

This idea really resonates with me. I’m a longtime user of JSDoc. Let me articulate why I find it useful.

What I wanted way back before TypeScript existed was JavaScript with static typing. TypeScript is mostly that. At least in the way I choose to use it.

I don’t use enums, namespaces, decorators, etc. This is significant because each of these features’ steps has an emit aspect; using one of these will require transpilation to create special JavaScript to represent a custom TypeScript-implemented feature. All other TypeScript features are erased by transpilation; there are no execution characteristics.

So, by subsetting the features of TypeScript, we can choose to use only those features that do not have an emit aspect. By making that choice, it’s possible to use just JavaScript if we’re willing to commit to using JSDoc syntax within JavaScript instead of TypeScript. There are many in the community who are doing this on sizeable projects like webpack already. We don’t lose type checking and we don’t lose refactoring possibilities thanks to editors like VS Code.

JSDoc is great, but it’s undeniably more verbose than writing TypeScript. If type annotations were to be adopted, we’d be able to write TypeScript in our JavaScript files. We’d also be able to use TypeScript to type check that if we wanted to. But we wouldn’t need to transpile our code prior to running — we could run our source code directly. Brilliant!

Controversy and compromise

Up until now, as we’ve looked at the proposal, the story has been one of JavaScript becoming “types tolerant.” As a consequence, the syntax of Flow/TypeScript/Hegel et al. would be considered valid JavaScript in the future.

This paints a picture of JavaScript as a dynamic language being changed to accommodate the sensibilities of those who favor static typing. If you take a look at the discussions on Hacker News and in the issues of the proposal, it’s clear that there’s a very vocal section of JavaScript developers who consider this proposal to be thoroughly unwanted.

While it’s unlikely that the most fervent dynamic language advocates will change their mind, it’s worth considering the nuance of this proposal. In fact, the proposal is a two-way street; to comply with types becoming JavaScript native, languages like TypeScript would likely make changes to accommodate.

Generic invocations and TypeScript

There’s a few cases which apply, the one that seems most significant is that of generic invocation. To quote the proposal:

One can explicitly specify the type arguments of a generic function invocation or generic class instantiation in TypeScript.

// TypeScript
add<number>(4, 5);
new Point<bigint>(4n, 5n);

The above syntax is already valid JavaScript that users may rely on, so we cannot use this syntax as-is.

So, if this proposal was to land, writing today’s style TypeScript in JavaScript would not work in the case of generic invocations.

If we read on in the proposal, it says:

We expect some form of new syntax that could be used to resolve this ambiguity.
No specific solution is proposed at this point of time, but one example option is to use a syntactic prefix such as ::

// Types as Comments - example syntax solution
add::<number>(4, 5)
new Point::<bigint>(4n, 5n)

These type arguments (::<type>) would be ignored by the JavaScript runtime.
It would be reasonable for this non-ambiguous syntax to be adopted in TypeScript as well.

This last sentence is significant. Let’s read it again:

It would be reasonable for this non-ambiguous syntax to be adopted in TypeScript as well.

While not being an absolute commitment, this certainly suggests that TypeScript would be willing to change its own syntax to align with something that was standardized as typed JavaScript.

Speaking personally, I don’t love the proposed new syntax, but I understand the rationale. A new generic invocation syntax is certainly something I could come to terms with. It’s good of the TypeScript team to be open to the idea of making changes to the language to align with the proposal. This is not at zero cost to them. This demonstrates that to allow this proposal to land, there will be compromises on many sides. It’s likely that Flow will be similarly affected, as well.

Conclusion

When you see the various discussions about the type annotations/types as comments proposal online, it’s clear that there are many strong feelings about it. The proposal hasn’t even reached stage 1 (of the potential 4 stages required for adoption). This may be a feature that doesn’t make it, or perhaps takes a long time to land on a mutually agreed design.

Personally, I’m hopeful that this will end up being part of the language. Not only do I like running raw JS, I see the benefits of being able to onboard people from JavaScript to TypeScript by allowing types to live directly in JavaScript.

It’s said that prediction is very difficult, so it is hard to know for sure what the long-term effects on the language and the ecosystem of this proposal might be. It would certainly lower the barrier to entry for using static typing with JavaScript, and as consequence, would likely lead to greater adoption and, hence, less bugs in userland. Time will tell.

Writing a lot of TypeScript? Watch the recording of our recent TypeScript meetup to learn about writing more readable code.

TypeScript brings type safety to JavaScript. There can be a tension between type safety and readable code. Watch the recording for a deep dive on some new features of TypeScript 4.4.

: Debug JavaScript errors more easily by understanding the context

Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

.
John Reilly MacGyver turned Dev 🌻❤️ TypeScript / ts-loader / fork-ts-checker-webpack-plugin / DefinitelyTyped: The Movie

Leave a Reply