A Proposal for Type Syntax in JavaScript
devblogs.microsoft.com
devblogs.microsoft.com
Google's V8 team (the team that owns Google Chrome's JS engine implementation) floated a proposal back in 2015 to implement a new JS mode that they called "Strong Mode."
https://docs.google.com/document/d/1Qk0qC4s_XNCLemj42FqfsRLp...
The idea was that you'd opt-in with a pragma like "use strict", but this time you'd "use strong". Strong mode would include a bunch of restrictions on how you'd write your JS. For example:
- class prototypes would be frozen; it would be forbidden to add properties at runtime, or to `delete` properties from a class
- The "new" keyword would automatically Object.seal() the object
- Arrays are non-sparse
Here's Google's 2016 announcement canceling the "strong mode" experiment. https://groups.google.com/g/strengthen-js/c/ojj3TDxbHpQ
> The main problem here was the interoperability with weak classes: we need to allow inheriting strong from weak classes and vice versa (or even alternating inheritance chains), because ruling that out would severely limit the utility of strong mode. But we could not find a semantics that would work without breaking either weak parent classes or weak child classes. In the end we had to give up on this one.
There were other problems, too, some of which are substantially mitigated now in 2022. Maybe it's worth a try now!
…but if you're hoping to hold up type comments until "strong mode" works, well, I just don't think it's gonna work.
And so, in practice, for the next five to ten years, we're either getting something pretty much like Microsoft's "type comments" proposal, or nothing at all.
I personally don’t want runtime type checking anyway[0]. Apart from my concerns[1], most of which are fairly navel gazey, I support the type comments proposal almost exactly as is.
My hope is that if others are successful asking for runtime type checking, they’re also asking for the corresponding performance problems to be addressed in tandem.
0: We already have runtime type checking: type guards. They have the advantage of being optional and explicit, which addresses performance concerns better than anything else I can think of.
I'm not sure what that last part means, but it doesn't mean that type annotations for function parameters are out of scope, right?
Either way, a big step, congrats to the authors for finding the courage to undertake it!
Edit: no it's not [1], though I'm still not sure what it does mean.
[1] https://github.com/giltayar/proposal-types-as-comments#type-...
Optional type annotations, packaged as a part of vanilla JS instead of requiring a build step, would be the only benefit. They'd be stripped out at runtime; the compiled JS would be identical.
The benefit: Sown by TS users or possibly some kind of of TS-lite using this subset of TS features.
Not benefiting: non-TS languages targeting the browser, like Elm, ClojureScript, ReScript, PureScript, Haxe, Nim, Kind, and <the next big language here>.
Why?
If anything, this gets vanilla JS users more comfortable with types in their JS. That seems like a win, and a good gateway to more strongly-typed languages.
Even if other languages aren’t actively benefiting, does it hurt them in any way? They wouldn’t even have to make any changes to their compilers; their compiled JS would stay just as valid.
The benefit is not to just Typescript users and advocates; the benefits are for everyone who wants more sanely developed JavaScript. Seems pretty good.
If anything it will lower the barrier of entry for new static type analyzers which can compete on how good their type system is, as opposed to the new language they are currently forced to create along side the type system.
By far and away the most popular compile-to-JS language is JavaScript itself, via tools like Babel. After that, you've got Typescript, which is still basically just JavaScript with some type annotations stuck to it. It's only after that that you start getting to languages like Cjs and Elm, which have more varied semantics and treat JavaScript more like a true compile target.
My reading of that would actually be the opposite: JavaScript is a pretty poor language to target, and, for all its flaws, it's easier to stick to languages that mostly have JavaScript-like semantics and syntax than opt for a different (and potentially better) language, but have to deal with the more complex debugging processes.
I think this will be different for Wasm, which has been explicitly designed as a compilation target, but that's still getting off the ground in terms of being suitable for general usage.
WebAssembly suitability for general HLLs is possible in the far future but there don't seem to be many eager consumers. People prefer JS interop, browser API access, and builtin high performance GC and a JIT of JS that can efficiently handle dynamic constructs with advanced compiler magic. Reimplementing those in your wasm targeting compiler would be a huge effort and complexity cost, and added bloat to compiled programs, even if wasm supported it.
The solutions we already have (typescript in comments, or lightweight dev servers like vite) aren't perfect, but they don't come with the gargantuan costs of types in JS.
At risk of running afoul of HN guidelines: did you actually read the post?
The proposal does not add type checking to JavaScript. It adds type syntax. There is a huge, huge difference.
Their proposal is to implement a comment-like type syntax. These types would be totally ignored at runtime.
This isn't "run TypeScript in the browser"; this is "add syntactic sugar to vanilla JavaScript to get the development benefits of types without a build step."
Yes, JavaScript engines would have to implement a parser for this syntax. Yes, this raises the barrier to building a new JavaScript engine.
Does that mean JavaScript should add no new syntax ever again? No, of course not. So the question becomes one of cost-benefit analysis. And I think the TS team has submitted an extremely compelling argument for the benefits.
Your comment acknowledges the existing benefits of types-in-comments; think of this proposal as a significantly nicer way to write those.
Yes? Yes. Maybe?
At least not in cases where the language can already do things adequately, and the change is just a cosmetic improvement. Annotating comments already works, changing that just to have a syntax that looks like type hinting but isn't just makes the language worse in my opinion. I'd rather they just bite the bullet and add optional type hinting, and make the syntax do what it appears to. Otherwise, the necessary complexity of typing Javascript should be kept entirely out of the language itself - including special syntax to make things easier. Deal with Typescript and its peculiarities and the build step, if that's what you want to do, and annotate types in the existing comment syntax, but Javascript is an untyped language and should be remain absolutely, even aggressively agnostic about such things.
My complaint is about adding type parsing in the browser. When I say "run typescript on the browser", I mean running code written in typescript syntax.
Please read my comment again with that in mind.
type RGB = 'red' | 'green' | 'blue'Considered a mistake by whom? they are valid Typescript.
It's just creating yet another flavor of Typescript... and why straight out adopt that type syntax? Because it will please Typescript developers and Microsoft?
All for what? being able to run that flavor of typescript without having to compile typescript files into Javascript? It would change nothing to the already complex build pipelines JS developers are already mandated to use for production.
I agree with the parent, this is a bad proposal.
What Typescript should do as an alternative is better support for JSDoc type analysis. I don't use typescript language, however I do use typescript compiler.
https://www.typescriptlang.org/docs/handbook/jsdoc-supported...
Does it defeat the purpose of typescript? No, typescript is both a language AND a JavaScript static type analyzer.
The developers of TypeScript. TypeScript aims to avoid adding anything to the runtime as a result of the type system[1].
> It would change nothing to the already complex build pipelines JS developers are already mandated to use for production.
You're right, it wouldn't change anything when you're building for production if you're doing things right. It would mean that you don't have to repeatedly compile your code while you're developing it though.
> What Typescript should do as an alternative is better support for JSDoc type analysis.
If you read the proposal you'll see they explain why expanding JSDoc is not ideal. Personally, I wrote a lot of JSDoc back before TypeScript and now I don't want to touch the stuff. TS is much more ergonomic and expressive.
[1]: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Desi...
One reason I've heard is that enums allow you to do rename symbol refactoring in all use sites, but like what How often do you need to do that? Once in a lifetime? Twice? And with string unions you can ofcourse also just rename it, TS will tell all the places that need fixing, and it'll take couple hours to go though 1000+ usages of it.
Let’s say I have an Icon component with props like:
type IconProps = { size: “s” | “m” | “l” }
And then let’s say I have a Button component but there’s no small size: type ButtonProps = { size: “m” | “l” }
If I use string unions I can happy assign one size to another: const Button = ({ size }: ButtonProps) => (
<button><Icon size={size} />
)
If I used enums (ButtonSize and IconSize) then that would not be possible and TypeScript would complain.So the question is: Is that a bad thing?
And the answer depends on your domain. If you have reason to believe that in your domain people are likely to map between two incompatible namespaces, then enums could help. And string unions could let through some bugs.
In my experience that’s never been a real risk so I prefer string unions.
The other thing enums can get you is you can rename the variable without changing the values. So if your strings are persisted to the database and you don’t want to change them, but you do want them to have a slightly new meaning, you could rename the enum key, and get the new name in code, while keeping the old string value.
I agree with what @erikpukinskis said but I'll also add that I just like the way they read. It immediately tells me that there is a limited number of potential values that can be put in a place. If I see some arbitrary string then I have to wonder, if only briefly, if any arbitrary string can be plugged in there.
// What does addTask take? Days of the week?
// Time-sensitive identifiers like you'd see in moment.js?
// I guess I'll check the definition or mock out a call to see
addTask('Sunday', newTask)
// vs.
// Ah, it takes a day
addTask(Day.Sunday, newTask)
It's subtle and not super important but it makes me happy. const options = Object.entries(MyEnum).map(([key, label]) => { key, label })"Large swathes" is really overstating it. The TS philosophy has been to avoid runtime behaviour for quite a few years now, and while there are a few features that made it in back when they weren't so strict about that yet, they wouldn't have added them today and have worked to add alternatives that do not modify runtime behaviour (like literal string union types). So it goes as much "halfway" as all TS development has gone for the past few years.
I would be for it if it was possible to keep TypeScript metadata somehow so I wouldn't need to create schema with Zod but with TypeScript. There was babel plugin like that for Flow and it was nice PoC and I guess similar could be done via some annotation with TSC.
It would actually be a net negative for JS, as it would allow the syntax to be shipped on sites, and then because it isn't actually checked at runtime you will inevitably end up with sites if the engines ever do start performing type checks, and so will directly prevent them from adding it later.
So as I understand things:
* This does not actually add type checking
* It does not support the full typescript syntax, so you don't get to skip a compile stage
* This prevents JS engines actually adding type checking later, due to breaking existing content
I am unsure why you believe performing the checks is untenable if TS already does them? The runtime checks should be relatively cheap and in perf critical code if it turned out type checks were too expensive there are a bunch of optimizations engines can do, or the impacted functions could drop the annotations.
Adding a significant parsing surface, with the related page load perf impact, with the potential (im going to be generous) to burn the syntax, this seems like nothing but downside.
TS runs the checks at compile time and compiles to JavaScript without annotations. There is no runtime cost.
> related page load perf impact
Type annotations would likely still be stripped by minifiers, just like comments. No change.
> burn the syntax
As a dev that regularly uses TypeScript, being able to use debug builds with annotations and copy paste TypeScript into the console would save a lot of time. This would be part of larger trend of adding type annotations to dynamic languages. Python, Ruby, and other dynamic languages have blazed the trail here. There are ways to add enforcement in the future if needed, just look at 'use strict';
If there were performance costs this can be mitigated very easily - to be able to do compile time checks means TS is designed largely around early binding, at least in the default case. A JS engine can do this trivially.
> > related page load perf impact > Type annotations would likely still be stripped by minifiers, just like comments. No change.
You're kidding right? You're proposing permanently changing the language, in a way that burns the type syntax for a feature that you're now saying will be stripped before a JS engine ever sees it?
> As a dev that regularly uses TypeScript, being able to use debug builds with annotations and copy paste TypeScript into the console would save a lot of time.
That sounds like a feature for your console, not the language
> This would be part of larger trend of adding type annotations to dynamic languages. Python, Ruby, and other dynamic languages have blazed the trail here.
Which enforced the types. Understand I am not opposed to the type annotations, I am opposed to this because it does not enforce those types. That means that broken content will exist, and thus burn the syntax.
> There are ways to add enforcement in the future if needed, just look at 'use strict';
Which will never happen again. As the person who implemented strict mode in JSC I can tell you that the cost of a mode switch is massive, in spec and implementation complexity. Adding another mode switch is simply not going to happen. The only reason I was ever ok with adding strict mode to javascript was the removal of the this conversion. No other part of strict mode would have warranted the cost.
I'm not one to argue, computers are fast. However, this would be a huge paradigm shift for the JavaScript ecosystem, where compile-to-JavaScript-tools like TypeScript are dominant. These tools have explicit goals to do type checking at compile time only and avoid generating anything at runtime. Runtime checking is redundant in this paradigm.
Now if you're arguing that the JS engines like V8 should be able to use type information for performance gains, I understand that wish. WebAssembly seems to be taking over the performance angle these days though, as it was designed from the start for that purpose, unlike JavaScript.
> You're kidding right? .. will be stripped before a JS engine ever sees it?
Yes. We're talking JavaScript here - billions of lines of which are compiled every year to ES5 ;-; with polyfills. Throwing away hundreds of useful features of ES2016+. This is par for the course, part of the JavaScript paradigm which this proposal understands.
> sounds like a feature for your console
That is fair, though it seems unlikely Chrome would adopt such a feature for their console without motivation. Perhaps.
> Which enforced the types
Python does not enforce types at runtime? You might be referring to something I'm not familiar with. Fired up Python 3.9. This works fine even though I'm passing strings for int.
def add(a: int, b: int):
return a + b
add("not ", "used") # 'not used'
> the cost of a mode switch is massiveBut earlier you said a JS engine can do this trivially? I don't understand how this is now difficult. Ultimately, if the types are enforced, you may as well go the full monty and support TypeScript in the browser, with Deno leading the way. Stop beating the JavaScript horse and all. That's the dream. This proposal is a compromise without a doubt, with the understanding that TypeScript in the browser is politically untenable at this point.
I really don't understand.
It wouldn't have to support any smaller granularity, and it wouldn't need to change parsing at all.
// TypeScript
function add(a: number, b: number): number { return a + b; }
// tsc output (JavaScript)
function add(a, b) { return a + b; }
// (ab)use by someone using my lib without TypeScript
add("abc", "def");
Now, addition is a contrived example because it'll still work with strings, but the point is that such usage violates my code contract because the contract was deleted during compilation. It's possible to do this: function add(a: number, b: number): number {
if (typeof a !== 'number' || typeof b !== 'number')
throw 'no';
return a + b;
}
But that's needlessly verbose, and eslint will complain about useless code. If enforceable type annotations could be added to JavaScript, that'd be a huge plus.It's Microsoft imposing their technology on the ECMA committee.
Ironically the same Microsoft that rejected ES4. We wouldn't be needing Typescript if ES4 was adopted 15 years ago...
edit: While this proposal doesn't have runtime typechecks, it's just one step toward adding runtime typechecks it isn't a stretch to think that if this passes, runtime typechecks might eventually be added to the language. ActionScript 3/JScript.net/ES4 had them, so it's perfectly possible for Javascript "with types" to add them as well.
I wasn't there for ES4; however, the type semantics of ES4 is a drastically differ from what's being proposed here. There is some information on the FAQ about why runtime types are a non-starter. (https://github.com/giltayar/proposal-types-as-comments/#why-...)
This comment probably won't count for much - actions speak louder than words. Hopefully we can demonstrate that we're being genuine here.
I mean this is a great idea, removing one more of the many many complexities in simply getting a nodejs package to browser and debugging (getting accurate source info and avoiding transpile errors are a PITA). JavaScript is an interpreted language yet 90% of the time it’s not even run interpreted. But “a proposal for Type Syntax in JavaScript” when TypeScript and JSDoc exist is just confusing and redundant.
1. While care was obviously taken to be type system agnostic, and while TypeScript is obviously the current incumbent, I think there could be more of a sign of collaboration with Flow, Closure and Hegel.
1a. They’re notably mentioned but conspicuously no one from those projects is listed as authors/champions. This suggests to me that they weren’t (substantially) consulted in drafting the proposal, which implicitly weights the proposal’s details in TypeScript’s favor if only by inertia.
1b. There’s surprisingly little “give” in terms of the few minor differences between Flow and TypeScript. I’ll grant the potential reservation of `opaque type`, but strongly suspect that’s because opaque types are already under consideration as a TS feature.
2. Omission of certain runtime-affecting constructs is certainly pragmatic, and not omitting them almost certainly a deal breaker for the proposal. But, it’s also quite likely to fragment TypeScript, effectively deprecating those parts of the language (which I’m sure the TS team would prefer to do explicitly, but well, that’s a much heavier lift).
3. This limited subset of TS syntax almost certainly is a real superset of JS, but there are some parsing disambiguation complexities to consider, particularly for type parameters on functions as well as (if ultimately included) function overloads and bare property modifiers. This also affects runtime performance, particularly at load time which is critical.
4. Omitting JSX makes sense in the current state of the world, but does it make sense for the goals of this proposal?
4a. JSX usage outside of TS has an almost totally overlapping motivating problem: the need for a build step. It doesn’t fit the proposal now, because JSX (usually) has runtime semantics, but they’re unspecified without build time configuration.
4b. Of all things that will fracture TS, this one is irreconcilable. I’m honestly surprised it’s even being considered by anyone on the TS team.
4 (conclusion). Given 4a/4b, JSX almost certainly should be a separate proposal, but I think it should seriously be considered as a potential prerequisite.
5. The goal of rapidly evolving TS and conservatively evolving TC39 is a balance that probably can be struck, mainly because the TS team has been incredibly good at keeping their syntax and semantics aligned with standards. But baking type comments into the language with a variety of new syntax blocks that syntax from being used for other proposals. For type-only syntax that’s obviously not a huge deal, but I’m imagining a fair bit of push back on reserving, say, the colon character in function positional arguments.
Probably more I haven’t thought of. And I want to reiterate I think this is a great idea in the abstract. I want to see it succeed. If the champions or any of the TS/Flow/Closure/Hegel teams, or anyone else here thinks any (or all) of these concerns should find their way into issues on the proposal repo, please feel free to copypasta or ask me to do same.
My general opinion is the complexity of JavaScript is increasing quickly.
Should JavaScript eventually end up like, say, TypeScript? I'm not sure.
> Why not define a type system for JS in TC39 instead?
> TC39 has a tradition of programming language design which favors local, sound checks. By contrast, TypeScript's model -- which has been highly successful for JS developers -- is around non-local, best-effort checks. TypeScript-style systems are expensive to check at application startup, and would be redundant every time you run your JavaScript applications.
> Additionally, defining a type system to run directly in the browser means that improved type analyses would become breaking changes for the users of JavaScript applications, rather than for developers. This would [violate goals around web compatibility (i.e. "don't break the web")][1], so type system innovation would become near-impossible. Allowing other type systems to analyze code separately provides developers with choice, innovation, and freedom for developers to opt-out of checking at any time.
> In contrast, trying to add a full type system to JavaScript would be an enormous multi-year effort that would likely never reach consensus. This proposal recognizes that fact, and also recognizes that the community has evolved type systems that it is already happy with.
[1] https://github.com/tc39/how-we-work/blob/cc47a79340a773876cb...
https://github.com/giltayar/proposal-types-as-comments#shoul...
https://github.com/giltayar/proposal-types-as-comments#shoul...
The idea is to avoid hampering competition, evolution, and innovation in the type-checking space.