Hacker News new | past | comments | ask | show | jobs | submit login
TypeScript 5.0 (microsoft.com)
606 points by dimitropoulos on March 16, 2023 | hide | past | favorite | 320 comments



Without Angular, we may very well not have TypeScript today.

The top thing listed in this release (TypeScript doesn't follow semver, btw) is about Decorators. I find the whole story about Angular's role in TypeScript early days to be very fascinating because I don't hear people talk about it anymore (just search "AtScript TypeScript" if you weren't around at the time). It was the Angular team that forced Decorators to be added when it was still stage 0. In exchange, they forfeited continued work on their would-be competitor to TypeScript.

Even without the Decorators thing, Angular was intensely popular and it made a big impact how they went "all in" on TypeScript, pushing a lot of early adoption. I know lots of people that first started with TypeScript because of work on an Angular project.

I think a lot of people don't seem to know/remember how touch-and-go TypeScript was in the very early days. There were many large projects that were all-in on FlowType, and others still that were pushing for Elm, etc. For example, it took quite a while for you to be able to do JSX _at all_.

Today, we just have TypeScript. Well. I do know one company that uses ReasonML (or isn't it called Rescript now?)

---

Historical stuff aside, I'm extremely happy we ended up with TypeScript over the others because the team is so incredibly awesome and dedicated. It's rare you see such a large project so well shepherded by such a talented set of individuals. It speaks volumes that the average tenure on that team is so high. We've seen some public apologies from the Flow team on this topic so I don't feel like I'm talking behind anyone's back if I say they ended up truly not being up to the task of being what TypeScript is today.


One of my most regretted decisions was evaluating Flow vs Typescript and going with Flow. Years later I had the opportunity to use Typescript and it was so nice comparatively. I've enjoyed it ever since. Of course, that was with years of development in its belt so maybe the experience wouldn't have been as nice if I went with it originally.


It’s hard to imagine if you weren’t in the React community at the time, but I feel like most people were pushing Flow at the time and choosing TS was quite controversial. I think it was the combination of Flow being from Facebook, and the “no build step/its just JS with annotations” thing which people made a big deal out of, but I never saw as a big differentiator.

I had a hard time convincing a couple of places I worked to use TS rather than Flow for greenfield projects, but I’d tried them both out and concluded Flow just felt too unstable - the server would consume all my memory and crash and it just didn’t feel like a good dev experience. I managed to convince them by showing them how you could strip out Typescript types in much the same way as Flow if TS did turn out to be a dead end. I also recall having vigorous debates at React meetups where I was the only person in favour of TS haha.

How times changed - you definitely can’t be blamed for choosing Flow at the time though as TS was the outsider. Also MS weren’t such a trusted brand for developers, people still associated them with proprietary stuff like .net/full fat Visual Studio and I think a few people had burned out in the MS ecosystem before and didn’t want to touch their stuff again!


> I think it was the combination of Flow being from Facebook, and the “no build step/its just JS with annotations” thing which people made a big deal out of, but I never saw as a big differentiator.

I put quite some time into evaluating Flow vs Typescript around 2016, and preferred Flow mostly because its type checking was more correct, and its bugs more straightforward. Typescript would happily compile all sorts of incorrect code even with its strictest configs, it was insane.

I eventually found my happy place with Scala.js.


This is true. I think TS arguably took a more pragmatic approach which allowed you to adapt existing codebases and coding styles more easily, at the expense (initially) of type safety. Obviously there have been many improvements since then


I made the same mistake, though our transition to TypeScript was actually very smooth. We just set up Babel to handle both, then converted one file at a time. Flow files functionally became untyped JS files until they were converted. And the syntax/semantics were close enough that converting code was really easy


Same. I spent the next few years doing major refactors from Flow to TypeScript (the largest that's public was for Insomnia: https://github.com/Kong/insomnia/discussions/3654).


Interestingly, both flow and typescript have roots in OCaml/F# respectively. Flow was written in OCaml and the creator of Typescript was on the F# team at Microsoft.


> creator of Typescript was on the F# team at Microsoft.

I actually think the bigger driver for TypeScript was Anders' history of working on C# and Delphi before that.


we started with Flow and moved to TypeScript a few years ago; converted 100k lines of code primarily with sed via regular expression find/replace; even then TypeScript was better but 80% of the better part was the library of available typings--Flow's support for lodash types was awful in comparison and we used lodash and lodash/fp all over the place


Flow was (and still is) awesome as a concept, but its main problem was that it felt as a side-project, which was supported for internal Facebook cases, but not really for open-source. TypeScript (back then) was a simpler language, but had a lot more manpower, which resulted in faster bug-fixes and more coverage of libraries.

If Flowtype had more resources from Facebook it could still be the dominating language


Don't worry - the Twitter web team also made this mistake. But they made it at a time when TS was popular instead of earlier when the two were equally popular.

Even well after TS was clearly the better option (as in 2022) the twitter web team opposed anything being done in Typescript like separate modules that would be a dependency of the web repo.

At least you recognize the better direction now.


With how much respect I have for TS now (its close to the gold standard in several categories for me), that would be a..... frustrating setup for me these days. TS just gets so much right, and is really a pleasure to work with IMO.


I'm curious what criteria you used to pick Flow vs. Typescript.


We picked Flow because we were using React, and Flow was from Facebook, so we assumed the support/integration would be tighter than with TypeScript. It also had some appealing goals like stronger soundness, and a greater emphasis on type inference than TypeScript had at the time

The main reason we switched was the tooling. The language server was slow in the best of times, would crash constantly, would memory-leak all over the place. Some of our devs stopped running it at all because it was so broken all the time. Then I tried out TypeScript and the speed and stability were breathtaking, and we never looked back.


I personally picked Flow because it was (and still is probably?) a much sounder and stricter type system than TypeScript.

However, keeping up with the constant breaking changes (I helped maintained a few complex flow typedefs) led to me getting disenchanted and burnt out with Flow.

Also, at one point a change was introduced where most (or all) internal errors would just cause a type to decay into `any`s, which frustrated me a lot.

I don't hold it against Flow though; its only customer is the internal React team, and it's a 0.x software too.

We've migrated our code to TypeScript later and it was a bit smoother sailing (although the typedefs for a few popular react ecosystem libraries were just... Abysmally wrong, it didn't matter at the end of the day; it was still easier).


Flow is more sound in a lot of ways (e.g. exact object types, by default!) but it also has huge holes (e.g. it has no equivalent to --no-implicit-any, the single most important strict flag in TS). Neither is strictly more sound than the other.


for a lot of people it was some combination of:

- Flow has more adoption (it did, for a while, in the OSS sphere)

- Microsoft is evil (remember, this was before everyone was using VS Code and loving it. (Also, tautologically, TypeScript has been one of the biggest things that changed this public perception for the people I hang around))

- Facebook is awesome (oh, how times have changed...)

- Flow has total type soundness as a goal (from the beginning, and through to today, TypeScript still lists this as an explicit non-goal)

- You literally cannot use React and Flow together (it didn't support JSX for what felt like an eternity and Flow did from the beginning (I think?).

- Flow had exact object types (TypeScript _sorta_ has this today, but most people don't know to turn it on because it isn't in the strict-mode family: exactOptionalPropertyTypes)

- Flow had more features (and was faster)

- DefinitelyTyped didn't exist yet, and it was easier to find types for Flow (but, let's be real, both had nearly nothing compared to today)


Total type soundness is not a goal of Flow.

For example, array bounds are not considered. Flow considers this program to have no errors, but TypeScript (with the correct option enabled) will:

  let m = [1, 2, 3];
  console.log(m[4].toFixed());
Also, DefinitelyTyped itself launched before Flow (2012 vs 2015), so the timeline on that point isn't right.


TypeScript tries to be sound too. It wouldn't really make sense to write a type checker if that wasn't your goal. As the person who wrote the "100% soundness is a non-goal" line in the design goals document, that's there to serve the same role as "Flow sometimes has to make a tradeoff" in the Flow docs in terms of clarifying whether or not 100% soundness is a design goal. For both projects, the answer is "no".

It's unfortunate that people have, over the years, decided that just being upfront about this fact means that TypeScript has a vastly different prioritization of soundness than Flow. We don't; both checkers use soundness as the default assumption when designing behavior.


> Flow tries to be as sound and complete as possible.

> https://flow.org/en/docs/lang/types-and-expressions/

That's what I was talking about re: Flow.

That's a really insightful comment you make about the "turn of the phrase" between how the two marketed themselves. Super interesting stuff.

also, apologies for messing up the timeline on the DT repo. My memory failed me there. Has it been that long?!?


I am still attracted by Flow which seems to have a simpler and stronger type system. However, Flow seems a bit unstable and the lack of support for TypeScript declaration files and widely used TypeScript syntaxes are stopping me from switching to Flow. I opened an issue for bridging the syntax gap between Flow and TypeScript [0]. Flow v0.201.0 renamed `$Partial` to `Partial` making Flow a bit more compatible with TypeScript. I hope it is just the beginning...

[0] https://github.com/facebook/flow/issues/8989


As an "old person" it still weirds me out to see Microsoft get so much praise for their open-source developer tools


TypeScript could be used with React at least since 2015, that's when I first used that combo in production.


it's going to be really hard for me to find now after all these years but there's a video somewhere on YouTube of someone at a conference showing their first attempts at getting TypeScript to work with JSX involving lots of special comments that they would compile out with babel or something (it's been a few years, hopefully I remember that correctly). It was pretty extreme (and cool!).


Flow still has some better sides:

* first class opaque type support

* exact object type

* correct variance, sound liskov oop

* no transpilation support (/*: */ and /*:: */) - so simple, so powerful; jsdoc ts doesn't compare, you can't import type star, when consuming libraries you have to increase maxNodeModuleJsDepth and other shenanigans; in flow you simply have access to all flow type language; why they don't want to do the same with ts is beyond me, it's like half a day of work to do it?

* nominal types for classes, structural for the rest - as it should be

* correct spread matching runtime behaviour

Flow is under active development. They recently completed ~2 years of work on local type inference which is a big deal. But dev team is not interested in external contributions - quite opposite to how ts development is done.

I did switch to ts as well but do miss above.


Great list!

Related to your point on supporting 0-transpilation workflows as a first class citizen, is the fact that Flow was explicitly just type annotations & checking, and aimed to introduce 0 runtime constructs.

This is something Flow did from the beginning and TypeScript eventually established as a non-goal after already implementing several runtime constructs that they can no longer afford to remove for backwards compat [1].

Though interestingly enough, Flow themselves recently announced they're going to start introducing runtime constructs, which is an interesting plot twist [2].

[1] https://github.com/Microsoft/TypeScript/wiki/TypeScript-Desi...

[2] https://medium.com/flow-type/clarity-on-flows-direction-and-...


In another plot twist TypeScript is supporting the Types as Comments TC39 proposal that would provide a true 0-transpilation workflow.

https://devblogs.microsoft.com/typescript/a-proposal-for-typ...


I remember doing a head to head comparison between Flow and Typescript back in 2015 or sometime around then, and flow came out top at the time. Main pull for flow for me was strict null checking and disjoint unions, which typescript lacked at the time.

About a year later typescript got strict null checks and discriminated unions, and from that point on typescript just kept improving, the community type defintiions kept getting better, and flow felt like it was stagnating and had lots of performance issues (esp on windows machines).

Flow actually still has a few notable features that typescript still lacks but are often asked for. For example, flow as `Exact` types that ensures that an object doesn't have excess properties throughout its lifetime - ergonomics are a bit awkward though.


Typescript in strict mode doesn't allow excess properties in an increasing number of situations. Typescript is still more structurally typed than nominally typed so there's no easy way to opt-in a specific type to stronger checks than average. The ergonomics are obviously different between Typescript and Flow. There are existing practical workarounds today to get more "nominally exact" types in Typescript such as using "brands" of various sorts (the most common is adding a private unique symbol to a type), though the ergonomics of using such branded types is also its own sort of awkward.


Those excessive property checks are pretty limited to basically the construction of object literals, and it does it because its normally an error if you have a very explict type but your literal expression doesn't match it. Typescript really does nothing to enforce anything around excess properties except for that narrow case.

Branding doesn't really enforce anything except what the user asserts is the case. Its not the same thing.


This was fairly early days (I want to say early 2016?), so its fuzzy now (and pre VS code going mainstream which has lowered TS friction pretty significantly) but I think a lot of it was a fairly basic pros/cons list with a lot of reading peoples opinions of using each technology. I think my ultimate reason was that Flow was comment-based and so was technically more "rip out"-able if I changed my mind later.

EDIT: oh, also as someone else mentioned it was a React project, so I'm sure that played a part.


Flow allowed gradual typing of the project and Typescript forced you to convert everything at once(at least the first versions of TS)


So it would seem that going with the flow turned out to mean not going with Flow; how confusing![1]

I went with TypeScript early on because the compiler chain felt better integrated than the alternatives. No messing with chains of babel translators and source mappings; just use one tool. Maybe I liked the syntax better, also. Although as I recall, in the beginning, somebody my company interviewed had set up TypeScript using Babel, and it was all very over-my-head and impressive. He turned out to be too good to work for us, but some of his ideas really stuck with me.

[1]This is why I dislike cutesy names for software projects. Just call it FacebookTypeAnnotatedJavaScriptFrobulator3003 (FTAJSF3003) or something.


> One of my most regretted decisions was evaluating Flow vs Typescript and going with Flow.

What caused you to choose Flow over TypeScript?


I remember this well, too.

Ironically, I think that angular is the framework that benefits least from actually using TypeScript. There is a separate templating language that's only type-checked if you set the right compiler options. JSX is much better suited for that. Being OOP like angular, it often requires opting out of null-safety or init-checks, because something is late-initialized. Having mutable instance state results in less opportunities to be more strict with typing.

Its good that the TS team decided to align with the ES standards. Initially (talking like v0.8.0), I had the impression that they're building a "C# for the web", being incompatible with ES. Maybe that's why enums and namespaces were added and initially, the boolean was named bool.


Enums have been proposed for JS for quite a while (there was a sort of enum in "the lost version" ES4 and a TC-39 proposal stuck in Stage 0 for years now [1]). Enums have always had relatively simple JS output that resembles a somewhat common pattern in JS. It was a "standard", just never really a deeply adopted "standard".

What's today called "namespaces" in Typescript was originally called "modules" and then "inner modules" (to differentiate from "exterior modules") and is a tiny simplification on a JS pattern that was extremely common at the time of Typescript's inception called at the time "IIFE modules". "namespace" is a very C# name chosen in desperation (when it became clear that "exterior modules" were the present not just the future of the language and "interior modules" was too confusing a term to live), but it's a very old (and used to be extremely common) JS pattern that TS gave a tiny bit of syntax sugar to. It was a "standard" that existed at the time that was (rightfully) killed by AMD/UMD/CommonJS/ESM, but it is hard to fault the Typescript team at the early 0.x days thinking the "Stage Wild" JS pattern would possibly live on for many years to come. (This is also relevant to the patch notes for Typescript 5.0 as 5.0 is the first version [!] to itself be written as [proper/"exterior"] modules rather than namespaces. There are some interesting stories, including in these release notes for why that took so long to transition and what the performance benefits have been post-transition.)

Even those things that didn't seem like ES standards have some history as JS "standards". It's not like TS 0.x/1.x didn't support "standards", it is more that the TS team mostly decided to focus more on later stage ES standards (more likely to be accepted by browsers) rather than trying to "prollyfill" Stage 0 and "Stage Wild" standards.

[1] https://github.com/rbuckton/proposal-enum


I remember this well.

From the outside, it seemed a fantastic example of collaboration between two separate (competing?) teams. It took humility and pragmatism for the Angular team to abandon AtScript and recognise TS as the better long-term bet.


I have often thought that it must have been tough to be on the Flow team. It was pretty good, and absolutely would have been "good enough" to catch on if it weren't for TypeScript. But ultimately the better language won.


Flow ultimately served only Facebook purposes and their work and the evolution of the language wasn't on par with TS.


And it doesn't hurt that the TypeScript team is so great. We've seen some public apologies from the Flow team on this topic so I don't feel like I'm talking behind anyone's back if I say they ended up truly not being up to the task of being what TypeScript is today.


Any links to their apologies? I don't understand why they apologized.



yes exactly.

The only other context I'll add is for people shipping Flow that I knew at the time they felt that this announcement felt like it came about two years too late. I had already switched to TypeScript long before because, in part, of the things they're apologizing for in that article which were evident long before being said out loud. I say that with great reverence and respect for the people that worked so hard on Flow, by the way, but that's the situation I experienced.


AtScript was always stillborn, and the Angular team absolutely could not have made a competitor to TypeScript.


Agreed, but the point I was making here is that Angular itself was very far from stillborn, it pushed early adoption of TypeScript really really hard which did wonders for TypeScript to be taken seriously.


Wonder why they didn’t go with the other google language Dart

Too foreign to frontend audiences? It has a lot of fans in flutter


There was AngularDart, don't know what happened to it.


Publicly it's dead, but it's alive and well and constantly getting updated inside Google.


>Today, we just have TypeScript. Well. I do know one company that uses ReasonML (or isn't it called Rescript now?)

One company outside Meta?


There's definitely a few around that are using ReScript; we've been using ReScript for production services for a bit more than 2 years now at Autobooks.

https://rescript-lang.org has a list too


yes! haha. They love it. I ping them and see if they want to identify themselves.


FB migrated all of its ReScript to Flow and deleted the ReScript.


> Without Angular, we may very well not have TypeScript today.

Typescript was started at Microsoft by the guy who designed Turbo Pascal and C#, so completely independently from Google's Angular team. Angular had its own "AtScript" and eventually ditched that. Typescript had experimental decorator support well before Angular Team moved from AtScript, because it was a ECMAScript proposal.

Angular had zero influence in the design or success of Typescript. Typescript however does support JSX optionally so one can claim that React DID influence Typescript's design decisions.


(I have worked on the TypeScript team since before its initial release)

While it's true that TS would have been started with or without Angular, I think OP is largely correct in the influence that Angular's embrace of TS had in TS's long-term success.

It's difficult to see these days, but if you were watching TypeScript's adoption numbers back when Angular made its TypeScript announcement, there is a marked inflection point when that happened. I was running lots of charts and graphs at the time tracking all kinds of data, and in every graph, you could see the Angular announcement in the chart; it's where the line went from kinda slow and linear to kinda fast and exponential. It was a true discontinuity in the second derivative.

We can't run history twice and see what would have happened without Angular, but I think the quantitative impact in this timeline is undeniable.


I chose TypeScript for a large project for the first time in early ~2018 and had no idea how much history I had missed. I presumed that the adoption was the other way around and that TypeScript was chosen because it fit the project's needs, but just as grandparent and parent suggest, AtScript was very much a thing.

Talk excerpt talking about Angular directives ~8 years ago! https://youtu.be/lGdnh8QSPPk?t=275


Even if that's true (it's definitely not how it was depicted at the time at conferences and from people on the Google side).. the point stands Angular was absolutely huge for it's day and they pushed TypeScript really really hard. It was a truce between two huge companies and it made waves in terms of people trusting Microsoft.

I have personally never been one of those mega Microsoft haters but I witnessed shouting matches in the office from people that we're hotly opposed to TypeScript being added to the codebase against others using Angular as a core motivation in many of their counter-arguments.

I'm super glad we have TypeScript one way or the other because the team is so dedicated to OSS.


> Even if that's true (it's definitely not how it was depicted at the time at conferences and from people on the Google side).. the point stands Angular was absolutely huge for it's day and they pushed TypeScript really really hard. It was a truce between two huge companies and it made waves in terms of people trusting Microsoft.

Angular.js was huge, by the time Angular version whatever because it was always changing, figured out what it wanted to be, most front-end developers already moved to React.

What Microsoft product uses Angular? Doesn't Teams uses Angular.js but not Angular? and what does a Google conference has to do with Microsoft? Nothing.


> Typescript had experimental decorator support well before Angular Team moved from AtScript

Do you have a source for this. My memory isn't so good but I seem to remember that the Angular team pushed to get this Stage 0 proposal implemented even though the TypeScript team usually waits until Stage 2 or 3 to implement.


Typescript was born of JScript. Check the history and the docs.


> and others still that were pushing for Elm

Still pushing for it. TS is like make-up on a pig, compared to Elm.

> I do know one company that uses ReasonML (or isn't it called Rescript now?)

Re* is also a better alternative imho. Better interop with JS/TS is very nice. But in place one could use Elm, I'd say it certainly wins in the "clean" reward.


god just think how good life would be if we had all switched to elm or reason instead.


Elm was never an option with how hard it is on "no js" policy


If only Elm was infinitesimally less pure.


there's not a day that goes by that I don't think about this


Yep. Frameworks play an important role in end user (developers) helps deliver software as quickly as possible.

Just like without Rails framework, we may very well not have Ruby today.

Also, without WordPress, we may very well not have PHP today.


True, Angular was crucial for adoption of TypeScript 5+ years ago but I think the community would've migrated nonetheless.


for sure for sure. that's kinda the point though: they already were migrating... to Flow. Lots of big projects are on the list that had to change course later because TypeScript won:

- Yarn

- Jest

- Luxon

- Gatsby

- Expo

- Styled-Components

- GitKraken

- GraphQL-js

and that's just off the top of my head. I'm sure there were many more.


Do you have the impression that Flow once had greater adoption than TypeScript, or that there was a movement from TypeScript to Flow? I think it was always behind in adoption, but of course, some projects used it anyways. From my memory, Flow always felt like the less conservative cousin and it was tempting to use it, but TypeScript always was the safer bet.


I have to disclose my potential bias here because I am these days very involved in the TypeScript world (with Michigan TypeScript). That said, I would never hide the fact that I was once a die-hard Flow person that.. died hard, haha. (and I'm very glad I did! more on that later)

In the very early days (in every circle I was in) Flow was ahead by every measure (known examples, excitement, adoption, features, speed, etc). You could point to real live running apps like GitKraken and Discord (hopefully my memory isn't failing me on those) that where shipping Flow very early. And you could use JSX! At the time there was literally no known project (especially OSS) actually shipping TypeScript. Not from Microsoft and not from anyone else. It was hard to tell if TypeScript was more of "embrace extend extinguish" (obviously with hindsight we can be absolutely certain that it wasn't that (unless we're talking about a decades long "long con" lol))

But there's something I could never have anticipated at the time that's more important to me today than any feature of the language:

The TypeScript team have done a breathtakingly amazing job of carrying the project through the years. TypeScript, as an open source project, is an absolute triumph: and I think it has everything to do with the people behind it.

At this point TypeScript is "good enough" in every way that really matters. If they consciously stopped developing the language and just focused on performance and bugs I think we should all call that a success. I really believe it's been handled _that_ well.

Meanwhile Flow went the other direction and there were bugs (in the 0.30 era, approximately) that had people pulling their hair out that were caused by the impedance mismatch of them making it for Facebook first and the rest of the world last. As far as I can tell, this really never changed (or at least, if it did, then maybe it was "too little too late").

Meanwhile, it seems like TypeScript has been managed with the literal opposite mindset: putting us in a situation where only one major project is left using Flow... React.

There are other projects like ESLint that have been... how do I say this in a not bridge-burn-y way... surprisingly reluctant to realize that TypeScript isn't some passing fad. But I'm confident that they're going to have their wakeup call sooner or later. The culture of JavaScript-land is just not loyal enough for gatekeeping a technology like TypeScript. Look at webpack. We all spent nights and weekends for years gaining knowledge on how it works and weeks per year getting familiar with it, then esbuild happened and we threw webpack away overnight like it never even happened. I believe the same fate awaits projects that deny the necessity of writing in TypeScript at this point in history, with the possible singular exception of React.


We have WASM dude. But TS people are in TS bubble for what it's worth.


WASM and TypeScript don’t really compare or compete. I’m not sure what you’re saying.


Until WASM can touch the DOM you’re still going to need JS (and hence, TS).


In fairness, rust-in-wasm has pretty complete DOM bindings. You can quite easily write DOM manipulation code in Rust without ever touching JS/TS. Whether that's a good idea is another matter.


How does that work?


web-sys[1] generates the binding code using the same IDL files that browsers use to generate their JS bindings to the C++ doing the actual work.

[1] https://github.com/rustwasm/wasm-bindgen/tree/main/crates/we...


You still need to run JavaScript functions to manipulate the DOM. "without ever touching JS/TS" seems misleading as that implies "no JavaScript code is executed".


When I read "touched" I don't think "executed". I think "edited". There is C code executed when I run JavaScript, but that doesn't mean that I had to touch it. Similarly, all the JS run when accessing the DOM from Rust is generated by the library. The user of the library doesn't have to touch it.


And objects. And so many more things.

Wasm is a cpu or low level operating system and not programming language


It's not worth anything. Are you writing raw WASM?


It's easy to miss, but I'm most excited for "--moduleResolution bundler" (https://devblogs.microsoft.com/typescript/announcing-typescr...)

My understanding is it should allow you to finally have TypeScript files that import other TypeScript files and include the file extension. This is important because, for one, it means Deno TypeScript modules and non-Deno TypeScript modules are now compatible (can import each other)! As long as no unavailable system APIs are used, of course

This has been a problem in a project I've been working on where a Deno back-end service wants to share some code with a Next.js front-end service. Currently, the shared modules are not able to import anything else, because Deno requires file extensions in import paths and non-Deno TS requires no file extensions in import paths


FYI, I just tested this, and it seems to work. I was a little concerned about the commentary in the PR about this new mode being "definitely not suitable for Deno", but I think what you want to do is the same thing I've been really irritated that I couldn't do before.

Namely, I have an Nx monorepo full of standard TypeScript code, but that code works with a many things: Node, Electron, frontend apps with Angular or Svelte or whatever, etc.

I want to use Deno for some new stuff, but Deno couldn't import any of that code without modifying that code so it no longer worked with all the non-Deno stuff.

For a quick test, I made a new default Nx monorepo and a regular TS library and a Deno library and a Deno app.

I changed all imports to use '.ts', and added this to the tsconfig.json:

    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
...et voila! My Deno code can finally import all that normal TypeScript code I have sitting around.

Haven't tested anything else yet, so I am not sure what, if any, issues those two changes above, plus including the .ts extension, might cause for other existing TypeScript projects.


>I just tested this, and it seems to work. I was a little concerned about the commentary in the PR about this new mode being "definitely not suitable for Deno"

FWIW I've been using version 5.0.0-dev.20230223 for a few weeks specifically for this feature - to share code between TS Vite/web and Deno projects - and it's been working without issue.


Exciting!

It's weird to see this:

> commentary in the PR about this new mode being "definitely not suitable for Deno"

I wonder what they meant by that. I mean, this doesn't magically make all TS code Deno-compatible, but nobody expects it to. What it does do is remove by far the most ubiquitous (and silly) barrier to TS code being Deno-compatible. There are others- system APIs, http imports. But this change allows a whole lot of code that doesn't use those to become compatible


Yeah, I didn't get that either, when I read it, but I think what andrewbranch is talking about there[1] is that you might still be able to have problems importing TypeScript code that is using the new "bundle" module resolution — because it may allow other things that Deno doesn't allow.

I don't think he is talking about the problem we are, which is that because Deno requires the .ts extension, Deno doesn't actually work with standard, non-Deno TypeScript code (that imports other standard TypeScript code).

I mean, until now.

I will run some more extensive tests tomorrow or on the weekend, and maybe go comment there if I have something useful to say.

But anyway, it seems to me that if, like me, your problem was "Oh no, I cannot import my code into Deno because I cannot add the '.ts' to my imports without breaking it in other TypeScript use cases" then that problem is solved in TypeScript 5.

I have the same exact problem at work, except the monorepo is bigger and there are a lot of people who might not be excited to change all their import statements just so my own experimental Deno tools can use their code but... step by step. This seems like a big step for my personal TypeScript projects. :-D

[1]: https://github.com/microsoft/TypeScript/pull/51669#issuecomm...


Since `.ts` extensions were in alpha, our repo has been set up so that you can switch between Node and Deno seamlessly in-editor and continue developing with no config or source changes.

Personally, I found all the `npm` integration stuff to be a bit overkill for what we were looking for, and honestly Deno's network requests while installing from npm were constantly flaking out in our CI. We ended up just disabling it via Deno's `--no-npm` flag (https://github.com/denoland/deno/issues/17916) and reverting back to a simple set of import_maps to get the node deps we needed. Works like a charm!

Feel free to reference if it's useful:

https://github.com/arktypeio/arktype


"bundler" is definitely not going to be the right resolution mode for using Deno; you may be better served by using ESNext or Node16/NodeNext (the "strictest" mode, really). The "who should use this mode" section here I believe is still accurate: https://github.com/microsoft/TypeScript/pull/51669


From the PR (https://github.com/microsoft/TypeScript/pull/51669):

> New compiler options

> - allowImportingTsExtensions: Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set.

To be clear, I'm not using tsc to build code for Deno. I've got module X which imports module Y, neither of which have any platform-specific dependencies. Right now if module X imports Y with the .ts extension, Deno can import X but tsc can't. If module X imports Y without the .ts extension, tsc can import X but Deno can't. With this compiler option, I should be able to include the .ts extension and allow both to (independently) import the same code


Ah, sorry; I brainfarted and missed the ".ts" part. I was thinking of the ".js" extensions, which are required in newer resolution modes (but are supported in older ones, and therefore using the strictest mode produces the most compatible code).


Interesting. I don't like all the build-tooling around front-end/Typescript. It's a common complaint I know.

I recently tried to modularize some code into a package that used Typescript import-path aliases. I don't know which step(s) I got wrong since there's multiple tools/packages, each with different versions, each with a different package needed for the appropriate build tool (x-for-webpack, x-for-rollup, etc). When building and re-using the module in some other project with a different build config I couldn't get it to build properly and wanted to just get back to working on my actual project.

I eventually just went and looked at the typescript repo for a reference build config thinking they'd have something easy to follow, but I just decided since they don't use import path aliases I won't either.

I hope all the tools go away that require me to look up things like: "is it an X type of module? if so export/bundle it this way" or "are you using an X type of module? if so import it in Y fashion and use build-pipeline plugins A B and C"


I have a strange take - I think Typescript types are better most other languages, including Java, C++ and Go. It's strange since TypeScript is adding types to a weakly typed language.

If we could push JavaScript performance to be another order of magnitude faster it wouldn't be necessary to use other languages, imho. Of course, pushing it that far without effectively creating a new one would be difficult, to say the least.


I used to think so too, until I tried Rust.

By comparison, JavaScript (and by extension, TypeScript) is still lacking fundamental features and the library ecosystem situation is pretty bad.

I wish there was a modern language that took all the good non-manual-memory-management things from Rust and added a GC and some immutable data structures. Error handling, enums, macros (with compile_error! / diagnostics API), traits / approach to OOP, embedded tests, doctests and the relentless determination to add examples in the docs in general... Everything in Rust feels like "oh, they got this right too", which hasn't been the case for any other typed lanuage I've used in the past 15 years (including typescript, my previous favorite; purescript; go; haskell)


You’re basically describing Swift. Swift and Rust had a ton of cross pollination during their early years which led to those features developing similarly but memory management is a lot less targeted for extreme efficiency like Rust. Hopefully as the language continues to mature, the feasibility to use it outside of apple platforms also does because I really enjoy the language design.


Does Swift has pattern matching and equivalent of Option/Result enums? I tried it casually but the documentation didn't feel great and things felt like they assumed familiarity with Apple's ecosystem.



Swift also supports enumerations with associates values, which is super useful: https://docs.swift.org/swift-book/documentation/the-swift-pr...


Thanks for the links - I did the Swift Tour and the language has indeed come a long way since I tried it last time; quite expressive and nice to write in. Hopefully it gains traction in non-Apple circles as well.


I do really like Swift, but isn't it usually significantly slower than Rust?

Swift seems more in the Go or Java class, rather than being competitive with C/C++ like Rust is. If that's so, Swift is still appealing as a pretty-fast, safe language with a much more sophisticated type system than Go or Java. But... TypeScript is too!

I have a feeling in terms of raw performance, JS is probably closer to Swift than Swift is to Rust. The important difference between Swift and JS isn't performance as much as which platforms/APIs/languages they can integrate with.

(Maybe I'm underestimating Swift's performance, though, or overestimating JS -- corrections welcome)


Agree. I feel like rust and swift will eventually converge with time, with swift providing more and more static guarantees and lifetime-related keyword, and rust improving its ergonomics. ( although recently swift definitely also went a completely different direction, adding as many concept as possible to the language)


I've been saying the same thing: We need an ahead-of-time compiled high level, business language. Something as efficient as Rust, but not designed for embedded microcontrollers or systems software.

I feel like the entire industry has had a "kneejerk reaction" to C++ and its abysmally slow compile times. Everyone jumped onto the interpreter bandwagon and ended up throwing the baby out with the bathwater.

It is definitely possible to have sub-second compile times for large, complex software! Just look at Jonathan Blow's Jai language. He can recompile and reload an entire 3D game engine in about that time.

We can have our cake and eat it to. We can have efficient, compiled languages and still have safety and fast compilation.


> Everyone jumped onto the interpreter bandwagon

I can only think of Ruby and Python that use a strictly interpreter mode (in their most common runtime).

Java, C# use a hybrid solution, but for all practical purposes they are running as extremely efficient machine code, how is it not “efficient business language”?


They're both JIT compiled, not ahead-of-time compiled. This severely limits the type of optimisations that they can perform, because they have to do it "on the fly" during runtime.


Being pedantic, both have AOT compilers readily available.

But sure, though I don’t really buy the argument that JIT compilers would “severely limit” the type of optimizations - there is no significant performance difference - if any - between AOT compiled managed languages and JIT-compiled ones. Sure, there are more constraints in case of a JIT, but it’s not like going in the other direction and letting gcc/clang chew 10x time more on the same program would give you a significant speedup, if any. Speculations (which are not possible AOT) may even reverse the fields.

D, Haskell, OCaml, Go are all in the same ballpark as Java and C#, and even JS, hell, they may be better.


The AOT compiled managed languages are hamstrung in no small part by their fundamental design, which was intended for virtual machines. Java and C# were designed in the 1990s. Since then, hardware has changed. For example, indirect calls are much more expensive in relative terms, and the memory bloat introduced by template metaprogramming is much less of a limitation.

Imagine a language as easy to use as Python, with the strong typing of Typescript, but designed from the ground up to always be fully compiled and hence running as fast as C++, but with build times measured in fractions of a second... fast enough to feel interpreted.


The biggest speedup one could get on modern hardware (in single-threaded context) comes from effectively using the CPU caches and in certain cases, vectorization. Clever use of these can give you 10-100x speedups.

This extra optimization is somewhat offset by the very point of a managed language: the programmer don’t want memory layout details to leak into the design/APIs, but in the rare case it is needed it can be done just as well with the escape hatches they provide (byte buffers, value types also bring you quite far). But business logic seldom involve these scorching hot loops to begin with, so there may not even be anything to optimize in this manner.


Sounds like OCaml.


Have you tried C#? Sounds like it would fit your needs perfectly, additionally benefitting from a huge ecosystem behind it.


I did, and I should've listed it. Its my third favorite typed language. At the time I was trying it, its type system wasn't as expressive as TS is today (no algebraic data types, no Result<T> error modelling in the type system etc), but LINQ with its clean syntax and ease of use is something I've missed in every language since (scala `for` syntax looks really awkward by comparison).


How does the verbosity in C# compare to Java's? And are big C# codebases littered with annotations and "dependency inversion" Java Spring boilerplate-y crap?

I ask because I have never delved into C# only heard it's Microsoft's Java. And I am no fan of the original Java at all so C# gives me pause.


Recent versions of C# have expressly been working to minimize/eliminate verbosity. Things like namespace statements (rather than { } blocks), global usings, top-level statements, record types (by default structurally compared, immutable data structures defined often only by their constructor and nothing else), and much more.

Similarly recent libraries such ASP.NET have been working hard to use more of these things to eliminate their own boilerplate. The latest ASP.NET templates use top-level statements and some global usings and are getting very lightweight in terms of starter-level code.

.NET does have annotations and dependency inversion, but those too tend to avoid boilerplate more than create it. Few annotations are "required", depending on your domain and which libraries you are expecting to use. .NET now has a single, mostly standardized dependency injector that almost everyone has agreed to use (Microsoft.Extensions.DependencyInjection) at this point. It intentionally only supports a bare minimum of Dependency Injection needs and is nothing like the Kitchen Sink approach of something like Java's Spring. (For instance, zero out of the box support for wiring Dependency Injection via XML files. .NET's DI is always code driven, often in a Startup.cs, Program.cs, App.cs, or Main.cs file.)

I've been increasingly finding in C# that I'm writing new code with zero templates and almost no snippets-expansion. (There will always be plenty of legacy code out there with much more verbosity, of course.)

The easiest suggestion is to try it for yourself. The `dotnet` tool is cross-platform, generally an easy install, and its `dotnet new` command will help you try many easy template types. You can relatively easily work entirely in VS Code today (as opposed to older versions of C# often "needed" the full bulky Visual Studio install for templates, language servers, and other stuff).


Thank you for such a well written and detailed response. Gave me a fair representation of C#.

I do hope to try it maybe in recent future.


C# is pretty much Java done right.

Speaking as someone who started with Java 1.2 and saw all the crap that happened to it.

C# is currently lightyears ahead of Java in every way, especially now that MS is not Micro$oft to people on the internet any more =)


> C# is currently lightyears ahead of Java in every way

Let’s not claim things that can’t be objectively proven. C# does have much more features than Java, but that is not necessarily a plus in case of a programming language. There are features that are definitely better, but I am not bought that the whole would be.

Plus, ecosystem-wise Java is much bigger and much more open-source. The runtime is also better on the JVM-side, though this is offset by C# expressing more low level details.


Also C# is really meant to run on MS servers (Azur). Yes you can compile C# asp core projects on Linux, but the hell of dll when the there are many projects in a solution is quit annoying. Development experience in C# without Visual Studio is quite cumbersome.


Nope. This is the M$ == bad era thinking.

I've been doing C# on a MacBook with JetBrains Rider for years. Some code runs in Linux containers, some on AWS Lambda.


Nope, see you're using Rider, because VS4Mac and VSCode only offer a subset of Visual Studio capabilities for .NET development.

I rather do .NET than Java, but the FOSS story isn't as Microsoft sells it, hence why on our agency .NET is mostly used on Windows projects, and usually loses against Java or node in UNIX like RFP, even after .NET Core reboot, because of dependencies and existing enterprise tooling.


C#11 defaults to nullability and has top-level functions giving it a big advantage over Java. The idiomatic Pascal case takes some getting used to but it's worth it.


Thanks for the overview. Hopefully I can give the language a shot when time permits.


We don't do annotation-oriented-programming in C#

DI is almost always in web, but via constructor instead of annotations.


C# doesn't have algebraic data types and pattern matching though, right? I'm not the same person, but at least for me that's one of the big advantages of Rust, and it's one of those features that outside of Rust you only really find in ML-family languages. I know it's possible to simulate it partially using sealed classes in some languages, but that's always seemed like a lot of boilerplate in comparison.


Pattern matching was recently added to C#. I can't say quite how well it's been embedded into the language. I always hear great things about F#, ADTs, pattern matching, nice type system and it integrates nicely into the rest of the .NET ecosystem.


F# is great. Simple, small, straightforward, great interop with .NET


Also, the requirement to manually order the compilation units (files) in an XML file to ensure the compiler doesn't get confused.

They had me at features, lost me at manual compilation ordering.


> it's one of those features that outside of Rust you only really find in ML-family languages

Or in Scala, technically not an ML, but is as close as you'll get in a nominally typed language.

    enum RGB:
      case Red, Green, Blue

    def log(x: RGB) = x match
      case Red => print("red")
      case Green => print("green")
      // error, not exhaustive
Obviously Scala can do a lot more, including GADTs, which Rust will never have until HKTs are supported.

Ton of powerful languages to choose from these days, good times...


C# has a lot of pattern-matching support today, including an expression-switch form: https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals...

C# can fake algebraic data types in some ways/some cases. Though if you are expecting to do a lot of it you are still better off in F# or C#/F# hybrid projects. (F# of course being a proper ML-family language.)


> I wish there was a modern language that took all the good non-manual-memory-management things from Rust and added a GC and some immutable data structures.

This basically describes all of the recent ML – perhaps more specifically OCaml – influenced languages like Kotlin, Swift, F#. (Rust is also heavily influenced by OCaml but its distinguishing feature is manual memory management with lifetimes.)

I do wish we had a good Linux/Posix option with as good of a package manager as Rust's cargo. Kotlin and Swift have some support but are largely focussed on the JVM and MacOS respectively. OCaml itself is of course an option here but its syntax and smaller ecosystem is a stumbling block. Reason is an attempt at a more familiar syntax for OCaml but that community is mostly focussed on transpilation to JavaScript with ReScript.


Its not quite that simple. Some of the things that Rust gets right are subtle, but typically punted by many other languages. Documentation containing fully working examples due to thos examples being run by doctests, for example, is kinda rare.

Here is another more subtle but important example: flexible API for error conversion, powerful language features and macros together enable the language, in conjunction with libraries like https://docs.rs/anyhow/latest/anyhow/ which gets you the best of all worlds in error handling: concise `?` operator for writing error handling code, eas to add additional message context (using `with_context` from anyhohw), differentiating between functions that can and cannot throw in the types (Result<T>), and the ability to get exception style backtraces from Result, too (that last one seems to be rare)

Another subtle QoL which is often overlooked when implementing macros: `compile_error!` (and soon even more powerful diagnostic API). With that, macros can report custom errors back to the programmer via the compiler and language service, rather than becoming an incomprehensible mess. For example, the peg parser macro will mark errors with infinite left-recursive rules that don't have caching set up with custom error messages.


And yeah I totally agree with you regarding `cargo`, in my mind thats also one of the things that don't seem to be related to the language but the quality of life improvements end up being huge.


For me that's Kotlin.

It's my general purpose language. I also use Rust everywhere Kotlin isn't suitable.


That’s basically F#. It’s great.


Have you tried Crystal? I got the same "oh, they got this right too" feeling about everything as well.


Crystal is excellent. I just can’t handle the sluggish compilation times. Has that improved?


Nope, but working with fewer/smaller dependencies (like Kemal) it seems more manageable. Also, the feedback from type system is much faster than the full build, so I've noticed I don't need to run the app to test every small change like I do with ruby. There is also an interpreter mode available for testing, but I haven't tried it yet since that currently requires building the language from source. I'm hoping the interpreter will solve that DX issue entirely once it's ready.


You just described OCaml or StandardML.

Or as others have said, F#, or maybe Scala. Or if you're feeling really brainy, Haskell.

But Rust was, in its early days, basically inspired by the ML type system, its type inference model, and its pattern matching facilities. The first Rust compiler was written in OCaml.


Haskell doesn't (or didn't last time I tried it) have the documentation and example culture of Rust.


Well he said better than most, not best of every.


Scala 3, or perhaps OCaml.


The jankiness of JS combined with the correctness of TS really is a powerful combo. Especially stuff like string keys and const arrays as enums make safe and readable code without a lot ceremony.

The big problem is the gap between TS and JS. User defined typeguards are a usable work around but it would be nice to be able to use a native isMyType function.

Also would be nice to have constraints natively in types. Like string lengths or max ints.


You can do all you list with effect-ts/schema.

Arguably the highest quality TS lib out there.

https://github.com/Effect-TS/schema


At first glance this looks similar to Zod (which is listed as an influence). What does @effect/schema do differently/better?

I feel like the name could use a bit of work for a start! "schema" is way too generic.


In our project at work, we've been using superstruct since 2021, but if we started today we'd probably choose Zod. We also use fp-ts, and it looks like effect-ts is a recent project by the same author, which certainly gets my attention.


The sibling post illustrate the problem. Lots of different libraries in this area and hard to know which one to pick. Also just kind of seems wrong to use a library for types in a language that is for types only.


this looks like a better documented io-ts


The author is the same of io-ts and t-comb, but capabilities go well beyond io-ts (including the experimental modules).

Being able to generate types, encoders, decoders, apis, guards and even arbitrary data is extremely powerful.


Good take if you ask me. When I want to flesh out an ontology, I find that TypeScript's type syntax more natural than anything else. Generally I find it to do an amazing job of bridging between a very expressive and flexible type system and a language that at runtime knows nothing about your types.

Want structural/duck typing? No problem; that's the default. Define new interfaces that old classes happen to implement without having to wrap them. I always wanted that feature!

Want nominal types? No problem; just declare a field indicating the type name!

  interface Square { classRef: "http://example.com/Shapes/Square"; width: number; }
  interface Circle { classRef: "http://example.com/Shapes/Circle"; diameter: number; }
  type Shape = Circle|Square;
(I like to express things in a way that can be easily translated to RDF, hence my use of URIs for type and attribute names)

Since TypeScript types don't runtime checks, you can even do it on atomic types!

  type USDollars = number & { [Symbol.for("http://ns.nuke24.net/Synx/unit")]: "USD" }
  type CanadianDollars = number & { [Symbol.for("http://ns.nuke24.net/Synx/unit")]: "CAD" }
  
  const someAmount : CanadianDollars = someExpressionReturningUsDollars; // Compile error!  Try doing that in Java!  I OFTEN WISH I COULD


Addendum to my nominal types: For the 'fake' nominal types (you can't put a symbol on a number!) I like to make those fields optional, which makes the type less of a lie. "If this number did have a [unit symbol] property, the value would be 'USD'". That will still prevent accidental USD <-> CAD conversion, but allows converting to/from regular numbers without explicit casting.

A pile of thoughts on the subject from back when I first came up with it: http://www.nuke24.net/plog/32.html


This is really great. I once spent a little time thinking about how to better represent RDF in Type/Javascript. These approaches seem like a good direction to go in. Would love to see these ideas developed further.


I’ve seen several TS users describe that approach as “branding” (or “flavoring” if it is optional).


Very cool


After using structural typing in TS, it's very hard to go back to nominal typing.


Better is hard to qualify but I'll add my 2 cents:

- for functional programmers TS offers lots of flexibility and power many other languages including statically typed and pure don't

- TS functions or methods that throw don't show up in the type system (they do in Java)

I hope TS keeps getting stricter.


> I have a strange take - I think Typescript types are better most other languages, including Java, C++ and Go. It's strange since TypeScript is adding types to a weakly typed language.

Typescript types don't exist at run time, at all. So it's easy to be better when your language is merely about static analysis as it will not enforce any sort of type system during runtime. Java, Go or C++ have different constraints.


that's a false equivocation in the sense that you just moved the goalposts by defining what "runtime" means.

There are no types whatsoever as far as processors are concerned. No types in assembly Not for Go or Java, not for C, and not for Rust. All you gotta do is keep compiling.


> that's a false equivocation in the sense that you just moved the goalposts by defining what "runtime" means.

I disagree, Javascript does have types and runtime type errors, just not all the ones Typescript compiler has, it makes Typescript extremely leaky as a Javascript abstraction. Typescript compiler cannot represent every single Javascript type combination either, it will would have to be a Javascript interpreter at first place.


Runtimes exist above processors, you know.

Here's types existing at runtime: https://go.dev/play/p/rc--yVzJ9bt


To be honest, that’s the “default” way of type systems - without runtime reflection capabilities type erasure is just a normal phase of an AOT compiler, why would you store types?


>If we could push JavaScript performance to be another order of magnitude faster

And it would speed up the TypeScript Compiler.

My bet is:

TypeScript typechecker in Rust:

https://github.com/dudykr/stc


TS is implementation-defined, it doesn't have a spec like C or Java.

It's simply impossible to keep up with TS for competing compilers.


One thing I wonder is whether a “strict mode” subset of TS could be compiled to efficient code. Disallow “any” and most of the dynamic and meta programming features of JS so it’s fully typesafe.

The type system is really nice, so even without being able to use the full range of JS hacks and escape hatches, I think it’d still be a nice language to work in.


TS by itself is not fully sound which could complicate such a compiler.

Example: https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgTwLI...

In the example above, the problem is that typescript allows casting function types to omit optional arguments, and also allows using a function with n arguments as a value where a function with >n arguments is expected


Yes, those sorts of loopholes would need to be closed. I was imagining a subset of the language, but there might need to be some breaking changes to cover cases like that. Maybe it would end up being so different that it would be an entirely different language? I don't know! But it's hard for me to accept that non-soundness in TS is necessary for TS itself rather than just an unfortunate consequence of the JS language and library that it has to build on top of.


If performance is the only concern to pick a language, world is far simpler.


That is a strange take. It sounds like you think the reason people don't use Typescript is because of performance and not what some would consider a irresponsible ecosystem of abandoned frameworks and a language that provides little actual safety while still being unpleasant to use.


It‘s not strange at all. I think them same. The type system of TypeScript is in practice much more useful than the one of C# or Java.

Nothing is abandoned and in any case typically only a small set of libraries is needed which should be carefully picked.


I agree TS is great. Not really multi threaded kills it for me though.


Threads are a function of the environment, and are available in TS and other compiles-to-JS languages:

Node.js: https://nodejs.org/api/worker_threads.html

Browser: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers...

However, threads are very rarely needed. The common use case in other languages is I/O, and JS environments handle that with async I/O—a superior choice, IMO.


Neither of those are proper threads, and they have various limitations placed on them.

And the language and the VM has been, and will be for any foreseeable future, single-threaded.


Deno supports vastly superior threading model (such as green threads). Again depending on what you are coding, threading may not be the best model. Look at Ngnix vs Apache (event driven vs threading).


> Deno supports vastly superior threading model (such as green threads)

Whatever it supports, Javascript-the-language has no concept of threads. And workers are basically external processes with a somewhat awkward event-based communication and certain limitations.

Whatever Deno uses internally to implement them has no bearing.


JFYI, apache httpd has had a similar event-driven request processing module for about 10 years, maybe more.

https://httpd.apache.org/docs/2.4/mod/event.html


Your regular reminder, that it’s just a regular release, since typescript don’t follow semver. After 4.9 goes 5.0


Is there a reason for this? Why even have the decimal if 4.9 -> 5.0 is just as significant as 4.8 -> 4.9. Just make it v49 -> v50 and remove the ambiguity. What am I missing?


One downside to having a version 4.10 is that some sorting algorithms put this release between 4.1 and 4.2 and not after 4.9.

But yeah, they should just go ahead and follow semver from now on.


Python went from 3.9 to 3.10 some time ago. And many people with YAML-based pipelines found out that 3.10 is turned into decimal 3.1, and ended up trying to build with an ancient version, because quoting was optional (and because YAML is a terrible language).



Why have decimals at all then? Just increment by whole numbers every release. It's the exact same thing except with the decimal moved over to a sensible place.


From the release notes, it certainly seems minor / backwards compatible.

> 5.0 is not a disruptive release, and everything you know is still applicable. While TypeScript 5.0 includes correctness changes and some deprecations for infrequently-used options, we believe most developers will have an upgrade experience similar to previous releases.


It does have some backwards-incompatibilities, but they're mostly minor/edge-cases


It has one of the largest internal rewrites in TS history (namespaces to ESM switch), so it's somewhat incredible its backwards-incompatibilities list is mostly minor/edge-cases.

(Which is to say that Typescript probably couldn't use semver if it wanted to because it has some extreme views on backwards compatibility, but also that this amount of codebase churn is absolutely semver breaking in the strictest semver senses even if it is mostly backwards compatible.)


So did the 4.x releases.


> How? There are a few notable improvements we’d like to give more details on in the future. But we won’t make you wait for that blog post.

the typescript project continues to set the bar extremely high in terms of changelog and clear communication. love you so much, drosenwasser and co!!

> Decorators are an upcoming ECMAScript feature that allow us to customize classes and their members in a reusable way.

huh. i thought this was a bit early to do this but i checked (https://github.com/tc39/proposal-decorators) and looks like they've moved to stage 3. curious how this is going to change library/api design in the long term.


I don't think decorators being a proposal matters at this point. I know I've been using them for 5+ years now, and libraries such as NestJS already use them extensively.

If you like them, you're probably already drowning in them. Me, personally, I could take them or leave them.


NestJS uses the previous, incompatible decorators implementation IIRC.


And, because it does so, you can fall down really deceptive holes due to the lack of typing around things that decorators apply to, like parameters.

I don't think that changes with stage 3 decorators, unfortunately. It makes NestJS really error-prone, in my experience, and it's a big reason I stopped making tooling for NestJS projects.


Yeah, it's going to be really "exciting" for Angular users as well in the next few years because Angular is drowning in those old "experimental decorators" which are not wholly compatible with today's Stage 3 decorators and the transition is going to be error prone and tedious. Up until TS 5.x it at least gave you a compile error if you tried to use decorators in code without the "experimental" flag, but now they will compile (except where they don't) even if you have the wrong flags but the behavior is so different between the flags it likely won't run. That's going to cause some interesting havoc.


What do you use decorators for? I've never used them and I'm curious what the usecases are. The logging thing looks helpful.


silly things, mostly. Redux used them such as "@connect" if I recall. But I haven't touched those in awhile. Other team members invented various ways to use them, but none ever seemed to make things clearer or save much typing.


yeah also feel quite ambivalent about them. theyre opaque syntax sugar. dont provide enough value to me personally but i wont begrudge others


I wish MS will fork Typescript into a new language altogether that transpiles to JS or compiles to WASM.

In that case, they can remove all JS weirdness from it, simplify it, add a proper standard library, and add other good parts from other (especially functional) languages to it.

My wish list for such a language (in addition to what is already in Typescript), in no particular order:

- Generic object literals (Why should generic type inference be limited to functions?)

- Deep support for an alternative to exceptions using a Failable<TResult, TError> type

- Localizable keywords (Why should programming languages be tools of cultural hegemony?)

- Dependent types, with no real distinction between values and types (At a certain point, Typescript’s type system begins to look like a separate Turing-complete language within a language. It’s probably time to do away with this dichotomy entirely somehow)

- Higher-kinded types (This is a pending issue in TS since 2014)

- JSX built-in without the need for any React-like dependency (No need to use it if you don’t like it; in the general sense, it’s just syntactic sugar for composing function invocations)

- No return necessary (the last expression in a block is returned). Thus every flow control construct _can_ (but does not have to) be an expression.

- No null, undefined, or any of all that, in favor of using a Maybe<T> type

- Proper decimal types (it’s shameful for a language to be without it now)

- A reasonably extensive standard library (Math, Stats, Collections (pull & push), String, Objects, Date/Time, Async, Functional, DOM, etc.)

That would be getting close to the perfect language for me.


If you're breaking backwards compatibility then my number one request is replacing UTF-16 with UTF-8 for strings and nothing else comes close.


You're asking Microsoft to undo a very Microsoft oddity that Microsoft seems to use just about everywhere. Good luck!


> undo a very Microsoft oddity

I don't think Microsoft are the reason why JS uses UTF-16 strings.


Windows finally added a UTF-8 locale. Sanity may yet prevail!


> In that case, they can remove all JS weirdness from it, simplify it, add a proper standard library, and add other good parts from other (especially functional) languages to it.

Have you tried Scala.js? It's pretty much what you're asking for - a first-class functional language with all the things you'd expect from that, with compile-to-JS as something that works and feels fully first-class (and good integration with TypeScript types via ScalablyTyped).


Scala.js seems interesting, and I will look into it more. But if it is so nice, why hasn't it taken off like TypeScript?


Scala was traditionally a backend language (and has a bit of an image problem, and some genuine community politics troubles), and isn't so well known on the front end side. I do believe it's genuinely the best language going though.


I think a big part of TypeScript's success is because it's just "JavaScript with types".


> Deep support for an alternative to exceptions using a Failable<TResult, TError> type

Is there a good library for this currently?

I've been looking for one and a Maybe library. The Maybe one I use is pretty small.


fp-ts (and most functional libraries) have a handy Either type just for this: https://gcanti.github.io/fp-ts/modules/Either.ts.html


That looks perfect thank you.


Massive release! `const` generic parameters in particular have been a god-send for our repo’s static inference (https://github.com/arktypeio/arktype) where previously we were forced to constantly rely on complex narrowing logic based on extends checks.

I look forward to the day when we support 5.0 as our minimum version and replace all of them with `const` generics for 1:1-inferred definitions like this:

const package = type({ name: "string", "version?": "number" })

Plus we’ll finally be able to remove the crazy hack we had to write to allow ts-morph to make type assertions on our codebase by transforming all of our `.ts` imports to `.js` in a virtual file system, which now that I think of it I’m probably looking forward to deleting even more XD

Great work, and looking forward to what comes next!


100%! I have a package that I've been running on nightly just to get this.

Felt a bit like the wait for a const generics in Rust (except here, it didn't take long at all)!


Question, is there anything decorators can do that higher order functions and higher order classes cannot already accomplish?

I typically dislike decorators because they are spooky action at a distance, and whenever I look at code in a language that has decorators, the code almost becomes a DSL of sorts.

Not that HoF and HoC don't tend towards the same problems, React used to be famous for how often HoC got used in the ecosystem and the confusion that caused!


"higher order class" is not a term I've heard before. I'm not sure what that could mean.

Decorators are syntactic sugar for higher ordered functions. They fundamentally have the same capability, just with better ergonomics.


Functions can return new class definitions- the "class" keyword represents an expression, not a statement.

As such, you could write a function that takes in as an argument a class, and return a new class definition that extends the argument you provided... Or any number of other dubious practices.


Sorry, I meant a function that rips a class open, messes with it, and returns a new class!


My favorite usage for decorators in java is for defining http requests, it lets you write stuff like this

    @GET("/users/{id}")
    function loadUser(id: int): Promise<User> {}

    @PUT("/users/{id}")
    function updateUser(id: int, user: User): Promise<User> {}


Can't fathom why annotation are used for this, except maybe as a holdover from years ago when Java didn't have certain language features. Other approaches in Java are much nicer and involve zero annotations. Example (from the Spark Java microframework documentation):

    path("/api", () -> {
        before("/*", (q, a) -> log.info("Received api call"));
        path("/email", () -> {
            post("/add",       EmailApi.addEmail);
            put("/change",     EmailApi.changeEmail);
            delete("/remove",  EmailApi.deleteEmail);
        });
        path("/username", () -> {
            post("/add",       UserApi.addUsername);
            put("/change",     UserApi.changeUsername);
            delete("/remove",  UserApi.deleteUsername);
        });
    });
All your endpoints concisely and composibly mapped out in code, and not smeared across dozens or hundreds of methods and classes. Not an annotation in sight; just plain old Java.


The code I posted above is used for client side apps, not server side. Client code typically has to accommodate calling different systems that use different patterns so having more flexibility in mapping endpoints to results is more important.


That looks worse.

Shorter code is not always better code


Did't know Java had a `Promise` type


That's not Java. I believe they're demoing in Typescript a pattern they like from Java/Spring/Quarkus/Micronaut.


Ah yikes, read that too quickly


I didn't know Java had a `function` keyword.

That is TS, not Java. Not sure why the gp is calling it Java.


They’re not . They’re saying it allows stuff like what’s it’s Java and the decorators in the TS code is exactly what it’s like in Java.


It has an equivalent but that code is typescript


> almost becomes a DSL of sorts

For mature projects with experienced, cohesive teams, that’s a good thing. You want a DSL that blends right in with your fully capable base language.

But yeah, just like with any powerful feature, people can get excited and carried away.


I think some people want things to be DSL-like. However, I don’t think, long-term, using a DSL-like syntax wins. Ruby was famous for abusing Ruby’s unique syntax to make everything into a DSL and it turned off newcomers. Similarly, Java’s DI frameworks meet a lot of resistance because of their heavy use of annotations. Ultimately, DSLs are separate languages. Most people don’t want to learn a new language.


In my experience as someone who has worked with dozens of new Ruby developers, I think the ability to create expressive DSLs was a significant net positive. It was much more likely to be a source of excitement than something that turned newcomers off. I don't think DSLs are or were a problem for Ruby (nor do I think they count as "abusing syntax").


The countless users of popular third-party frameworks are the opposite of a “experienced, cohesive team” so I agree with you that it can and has caused problems there.


The EcmaScript implementation of decorators is quite nice—it’s just syntactic sugar for a higher-order function call. Much better than the spooky reflection-based approach you see in some other languages.


> I typically dislike decorators because they are spooky action at a distance, and whenever I look at code in a language that has decorators, the code almost becomes a DSL of sorts.

Yes, decorators are not evil by themselves, but they allow people to make poor choices easier. Having to debug decorators, fiddling with order of execution + scope binding/closures when setting multiple decorators, can (not sure!) become a nightmare; depending on the code base. Or we just go straight to the hibernate spaghetti. Imagining having to use three decorators with the same name, but different functions, makes me not to want to work with this.

Having worked with (old) Java EE and Jakarta code bases, I've seen decorators being abused in 300 ways and breaking ABI in 600 ways, so let's hope we stay sane this time.


They're the same, but decorators encourage a programming style that is much easier to follow than HOF do. This way of using them is pretty standard in Python and it works well, imo.


Decorators enable metaprogramming over class fields. Nothing before them has done that.


The answer is obviously not since decorators still compile to higher order functions.


Yes - decorators are syntactically nicer :)


I’m always surprised by how much others like typescript. Out of all the languages I have to use, typescript is the one that feels the most like pulling teeth. Maybe I just don’t know how bad it truly was to work with a really large JavaScript project without types and that’s why people love it, but without that experience it just feels like all the hassle of types without most of their benefits.


Your experience probably differs from people who like TS. Personally I don't feel any hassle from using it and I couldn't even start listing all the benefits it offers. Even for very small personal projects, first thing I do is installing TS.

Not saying it's your case, but I noticed that a lot of people who hate TS like to use techniques and patterns that are usually considered bad practices, which often trigger errors in TS. Mutating an object to add a new property, mutating an array to add an element of a different type, processing apples and oranges in the same function without using generics or the correct union type, etc. Code works but TS doesn't like it and forces them to rewrite it properly and it feels like a hassle with no benefits.

I also noticed that lot of people who don't like TS think they need to type everything (every variable, every function's return... which is obviously inconvenient) instead of relying on types inference. There is usually very little to type in TS (compared to Java or C# for example). The two main things are the parameters of your named functions and your external data (i.e. the fetch responses). Almost everything else can be inferred.

I also observed that some people refuse to use VSCode (or any code editor with a good TS support). So they don't see any of the benefits while coding and think it's totally useless.


>Not saying it's your case, but I noticed that a lot of people who hate TS like to use techniques and patterns that are usually considered bad practices, which often trigger errors in TS. Mutating an object to add a new property, mutating an array to add an element of a different type, processing apples and oranges in the same function without using generics or the correct union type, etc. Code works but TS doesn't like it and forces them to rewrite it properly and it feels like a hassle with no benefits.

Not sure, people who like TS are usually for large corporate projects with OOP background. Most functional lispish type developer would be fine just using ES6 and above.


I feel the same, but when I complain I get blamed. Or someone chimes in with an illegible type/interface definition that solves my issue but is completely out of reach for all but language experts, which in other languages is not an issue.


Can you provide an example of something you'd see in common use that is 1) not provided by an existing library (such that you are consuming a complex type rather than writing it) and is 2) "out of reach for all but language experts"?


FWIW, when I first started getting into TypeScript from plain JS in 2019, I had a similar feeling. Part of it was that I was dealing with some object types of libraries I was using that were already quite complex. And, given all of the advancements in TS over the past few years, writing new types can be quite difficult - there is just a ton there. E.g. TypeScript's type language is famously turing complete.

That said, I absolutely love TypeScript, even just for small personal projects. The value I get from VSCode typeahead, and being able to look up the expected fields on, for example, parameters or return values of a function is invaluable, never mind its power in refactoring.

I guess I would recommend that, if in the beginning, you feel a bit overwhelmed by TS and can't yet see its value, just give it some time. At least, that's what happened to me.


I think that TypeScript, properly managed with things like zod or typebox, does an excellent job of giving me those benefits of types that I've come to expect while also having a really, really first-class development experience that reminds me (unsurprisingly?) of how actually-delightful using C# was in its heyday, with ReSharper and Visual Studio making it enjoyable. I genuinely don't feel any hassle around using it; the way that I can start with type signatures and then make the red squiggles go away is a really comfortable way to write code for me.

For me, I was out of the JavaScript cinematic universe for a long time (from about 2011 to 2018) and TypeScript was my entree back in. What got me deeply into it, and has kept me there, is not that it's better than JavaScript, but that, for my purposes, it's better than my at-the-time common languages: Ruby, Java, and C#. It gives me most of the linguistic flexibility that I'm used to with Ruby, while preventing me from dealing with the worst thing about it (Other People's Ruby being high on my list of terrible things). At the same time, it provides a level of "fall into the pit of success" structure around the type system that's superior to Java or C#, while not being as difficult to access as Rust can be.

(I like Rust, too! But the level of experience and skill necessary for somebody to be a "TypeScript operator" remains low, while letting those folks leverage really nice tooling built by folks like me who write TypeScript tools and libraries.)


I'm curious about what languages you're comparing it to. My other experience is with C# and Go, and I find Typescript's type system to be much more expressive and natural to work with.


I hope your editor supports TS.


It is truly bad to work with large Javascript projects without types. But I'm curious, what benefits of types do you feel you are missing in Typescript?


Typescript is a good try, but ultimately our favourite JS runtime errors still happen, even with all the strictest ts settings. Syntactically it is quite clumsy, you can see the uninspired mix of C++ and Java in there. The typings now although people are doing lots of cute things with them, don't do some really fundamental things. For example there is currently no sensible way to type a non empty array in Typescript. Also the discriminated union with the identifying string literal in there is not terribly elegant.

Personally I wish either Rescript or Purescript gained adoption instead.


> there is currently no sensible way to type a non empty array in Typescript

If we remove the "sensible" requirement:

    type NonEmptyArray = [any, ...any[]]


Or better:

    type NonEmptyArray = [unknown, ...unknown[]];
    const nea: NonEmptyArray = [];
    //    ^^^ Type '[]' is not assignable to type 'NonEmptyArray'.
    //        Source has 0 element(s) but target requires 1.


Can you map over this and preserve the NonEmptyArray type?


Do you mean pass in a type?

    type NonEmptyArray<T> = [T, ...T[]];


I mean if I have a NonEmptyArray say xs, and I then xs.map(x=>2*x), will the result of this expression be a NonEmptyArray ?


No, unfortunately it will just be number[] as the return type.


The missing feature I want most is type narrowing across chained functions like:

arr.filter(a => a.kind === „bar“).map(a => /* a should now be of type Bar just like in a branch */)


It was would be nice for this to be automatic but filter does work like this it you pass a type guard as the predicate:

``` arr.filter((a: A): a is A & { kind: 'bar'} => a.kind === 'bar') ```

If you define:

``` function isKind<K extends A['kind']>(kind: K): (a: A) => a is A & { kind: K } { return (a): a is A & { kind: K } => a.kind === kind; } ```

Then you can write:

``` arr.filter(isKind('bar')) ```

and get a properly narrowed return type array


Nice, thank you



Wait, are you saying they added that in this release? That's awesome!


This is not part of this release.


Congrats to the Typescript team.

The rate of innovation from them is really impressive.

Fantastic to finally see Decorators shed their "experimental" label (they've been the stable backbone of Angular for years).


It's not the same decorators that existed before that were added.

They don't have the same features. The new one are based on the upcoming EcmaScript decorators.


Also, Angular relies on parts of the "experimental" decorators that these Stage 3 decorators do not support, so Angular likely will continue to use the "experimental" compiler flag for some time to come, with the added twist that some uses of decorators will now also compile without the flag but will exhibit different behavior and won't run correctly, which may cause all sorts of new headaches for Angular developers.


I just posted this in our FE channel at work

> Note that the ECMAScript decorator spec now supported in TS 5 doesn't support decorating method arguments, unlike the experimental decorators TS has had an that Angular uses e.g. @Inject, @Self, @Optional etc. I'm guessing that this is part of the motivation behind the inject() function, where you can pass flags to it as a second argument to support cases like self or optional - it gives them a path to moving to the new-style decorators in the long-term.

AFAIK argument decorators are only used for constructors, and that usage can be replaced by using inject() with flags, so a migration would be possible even today - although I'm sure there would be plenty of as-yet uncovered subtle implementation quirks!

The obvious headache TS 5 will introduce is that failing to follow a decorator name with () will now attempt to treat e.g. Inject as a new-style decorator and cause a type error of some kind, which is likely to be confusing. I'm guessing the Angular compiler will have to do something to manage this, even if it's just ensuring there's a useful error message.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: