4,031 karma · joined January 16, 2015
@drosenwasser on Twitter.
DanielRosenwasser on GitHub.
esbuild doesn't rely on TypeScript at all, so there's no issue there.
With tsdown on the other hand, it depends on if you use --isolatedDeclarations. If not, you can install TypeScript 6 side-by-side (instructions for this are on the blog)
Unfortunately it has continued to make the rounds for about a decade now and gets re-posted every year or so.
We (TypeScript) used to do this for Visual Studio prior to tmLanguage. It was nice because we didn't have to write a second parser. Our parser was already error-tolerant and incremental, and syntax highlighting just involved descending into the syntax tree's tokens. So there was no room for divergence bugs in parsers, and there was also no need to figure out how to encode oddities and ambiguity-breaking logic in limited formats like tmLanguage.
This all predated TSServer (which predated LSP, though that's coming in TypeScript 7). The latency for syntax highlighting over JSON was too much, and other editors often didn't make syntax highlighting available outside of tmLanguage anyway. Eventually semantic highlighting became a thing, which is more latency-tolerant, and overlays colors on top of a syntactic highlighter in VS Code.
The other issue with this approach was that we still needed a dedicated thread just for fast syntax highlighting. That thread was a separate instance of the JS language service without anything shared, so that was a decent amount of memory overhead just for syntax highlighting.
- through builds (https://www.npmjs.com/package/@typescript/native-preview), or
- through the editor extension (https://marketplace.visualstudio.com/items?itemName=TypeScri...)
____
Language choice is always a hot topic! We extensively evaluated many language options, both recently and in prior investigations. We also considered hybrid approaches where certain components could be written in a native language, while keeping core typechecking algorithms in JavaScript. We wrote multiple prototypes experimenting with different data representations in different languages, and did deep investigations into the approaches used by existing native TypeScript parsers like swc, oxc, and esbuild. To be clear, many languages would be suitable in a ground-up rewrite situation. Go did the best when considering multiple criteria that are particular to this situation, and it's worth explaining a few of them.
By far the most important aspect is that we need to keep the new codebase as compatible as possible, both in terms of semantics and in terms of code structure. We expect to maintain both codebases for quite some time going forward. Languages that allow for a structurally similar codebase offer a significant boon for anyone making code changes because we can easily port changes between the two codebases. In contrast, languages that require fundamental rethinking of memory management, mutation, data structuring, polymorphism, laziness, etc., might be a better fit for a ground-up rewrite, but we're undertaking this more as a port that maintains the existing behavior and critical optimizations we've built into the language. Idiomatic Go strongly resembles the existing coding patterns of the TypeScript codebase, which makes this porting effort much more tractable.
Go also offers excellent control of memory layout and allocation (both on an object and field level) without requiring that the entire codebase continually concern itself with memory management. While this implies a garbage collector, the downsides of a GC aren't particularly salient in our codebase. We don't have any strong latency constraints that would suffer from GC pauses/slowdowns. Batch compilations can effectively forego garbage collection entirely, since the process terminates at the end. In non-batch scenarios, most of our up-front allocations (ASTs, etc.) live for the entire life of the program, and we have strong domain information about when "logical" times to run the GC will be. Go's model therefore nets us a very big win in reducing codebase complexity, while paying very little actual runtime cost for garbage collection.
We also have an unusually large amount of graph processing, specifically traversing trees in both upward and downward walks involving polymorphic nodes. Go does an excellent job of making this ergonomic, especially in the context of needing to resemble the JavaScript version of the code.
Acknowledging some weak spots, Go's in-proc JS interop story is not as good as some of its alternatives. We have upcoming plans to mitigate this, and are committed to offering a performant and ergonomic JS API. We've been constrained in certain possible optimizations due to the current API model where consumers can access (or worse, modify) practically anything, and want to ensure that the new codebase keeps the door open for more freedom to change internal representations without having to worry about breaking all API users. Moving to a more intentional API design that also takes interop into account will let us move the ecosystem forward while still delivering these huge performance wins.
Mentioning this because I did assume that from the title.
Instead I'll just mention that I always welcome thoughts on some of the challenges teams encounter when writing in TypeScript. It helps make the language better. If there's anything you often hit, you can comment here, create an issue on the issue tracker, or reach out to me at Daniel <dot> Mylastname <at> microsoft <dot-com>
The thing to keep in mind with these different libraries is that they are not necessarily perfect substitutes for each other. They often serve different use-cases, or can be combined in various ways -- possibly using the techniques directly and independent of the libraries themselves.
I understand the desire here. Runtime type checking is often necessary for data validation, and we can see lots of libraries developed to help fill the gap here. But I think the fact that there are so many libraries with different design decisions is pretty indicative that this is not a solved problem with an obvious solution. We knew this going into the early design of TypeScript, and it's a principle that's held up very well.
What have been happy to find is that we've grown TypeScript to be powerful enough to communicate precisely what runtime type-checking libraries are actually doing, so that we can derive the types directly. The dual of this is that people have the tools they need to build up runtime type validation logic out of types by using our APIs. That feels like a reasonable level of flexibility.
aozgaa has already answered this one - but yes! If you have a concrete suggestion, that's fair game and we can brainstorm on the issue to think of something. We might not come up with something general enough to implement, but it's often a good seed to plant.
> which in turn broke a type used from ProtobufJS.
I am curious to hear what sort of issue you ran into. Was this the Apollo fork (https://github.com/apollographql/protobuf.js), or the original?
i. Dependency management is indeed frustrating. TypeScript doesn't create a new major version for every more-advanced check. In cases where inference might improve or new analyses are added, we run the risk of affecting existing builds. My best advice on this front is to lock to a specific minor version of TS.
ii. My anecdotal experience is that library documentation could indeed be better; however, that's been the case with JavaScript libraries regardless of types.
iii. Our error messages need to get better - I'm in full agreement with you. Often a concrete repro is a good way to get us thinking. Our error reporting system can often take shortcuts to provide a good error message when we recognize a pattern.
iv. Compilation can be a burden from tooling overhead. For the front-end, it is usually less of a pain since tools like esbuild and swc are making these so much faster and seamless (assuming you're bundling anyway - which is likely if you use npm). For a platform like Node.js, it is admittedly still a bit annoying. You can still use those tools, or you can even use TypeScript for type-checking `.js` files with JSDoc. Long-term, we've been investigating ways to bring type annotations to JavaScript itself and checked by TypeScript - but that might be years away.
I know that these points might not give you back the time you spent working on these issues - but maybe they'll help avoid the same frustrations in the future.
If you have any other thoughts or want to dig into specifics, feel free to reach out at Daniel <dot> MyLastName at Microsoft <dot-com>.
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.
That page can give some guidance on diagnosing what the compiler's time is going into. Most users won't need to do this, but there are plenty of bigger teams/organizations that are really invested into TS and do want to keep their build times lean.
- union types aren't always bad, DON'T avoid them
- DON'T feel the need to annotate every single thing
Please apply a critical lens as you read through this page. The document was meant for users who've hit rough perf issues, and we tried pretty hard to explain the nuances of each piece of advice.
So glad to hear that you're happy with the release, and thank you for the kind words.