I was unsure of Typescript at first, but it has become an absolute joy and absolute pleasure to develop in.
I was reluctant to introduce a build step (having got used to the simple refresh cycle of a browser plus vanilla javascript) but I am pleased to say that this is a bit of a non-issue now with modern tooling like esbuild et al.
I still avoid NPM like the absolute plague, but the good news is that tooling like Deno have made typescript first server-side development in the ECMAScript world a total joy.
I've said it before - it's like floating through the air. So serene, so expressive. A true joy.
The thing is, TS has a very pragmatic type system [1], with multiple opt-outs or workarounds if necessary.
"Decent type systems" are usually in your way (especially if you want to quickly prototype something), and despite "decency" have abysmal tools (like "hey I've changed this function signature, update all call sites etc.)
[1] It's getting more and more complex, but even if you write something like this, it's still aiming to help you:
I like type systems as you can design with types and check things tie together before even running the code. This gives you very fast iteration cycles compared to CMD-R refreshing the browser/code.
Some issues with TS are:
- There is no hard guarantee at runtime as everything is partially/optionally typed (including your deps).
- The types are not used to produce faster code at runtime (like in a compiled language).
It is almost like TS leaves half of the benefits of type checking on the table, obviously by design by being a JS superset.
You can get very close if you place fine-grained, well tested, type guards at IO boundaries, wrap poorly typed or overly dynamic dependencies, and have good discipline about internal boundaries. It’s a lot of work up front in a greenfield project, but it really pays off.
> The types are not used to produce faster code at runtime
Not directly, but in my experience good up front interface design tends to produce either immediately optimal JS (eg it tends to be monomorphic) or makes optimization much easier (as it makes any refactoring easier).
Edit:
> I like type systems as you can design with types and check things tie together before even running the code. This gives you very fast iteration cycles compared to CMD-R refreshing the browser/code.
I’d be remiss not to emphasize this! And not just skipping the reload cycle, it lets you skip a whole lot of test runs too. Type error in editor? Yep, those tests are gonna fail too.
>> - There is no hard guarantee at runtime as everything is partially/optionally typed (including your deps).
Most typed languages don't have hard guarantees at runtime. With java, using reflection you can do all kinds of weird things at runtime. C++ has fewer guarantees because there is no VM and can't even introspect objects to see their real type. Dependencies are a real issue with TS, but many libraries are written in typescript and the type files are really robust.
>> The types are not used to produce faster code at runtime (like in a compiled language).
The V8 intepreter will actually do this at runtime. Usually it takes less than a second for v8 to optimize the javascript, and it will recognize that many functions and objects use the same interface and optimize appropriately. If you think about it the types only help so much because once you have the code graph, it becomes really easy to find the type and optimize the memory layout.
I personally can’t agree with that. TS catches me writing numerous bugs a day, the structural typing plus static analysis is a superb combination. If your code base is in JS without types, you probably have many bugs, you just don’t know about them. Especially around undefined/null handling.
TypeScript also allows me to refactor fearlessly, which substantially improves the quality of my code as I can do mini-rewrites without worrying about breaking interfaces.
I see this sentiment a lot but I honestly can't think of any non-trivial bugs that TS has caught for me. 99% of the bugs it catches I would see 1 second later when my page hot reloads and crashes.
well I can, I wrote a year-long js project that I abandoned as refactorings gradually turned into eternal bug-fests. I started from scratch in typescript, and the project is now 3x the dead project, with none of the bugs. and refactorings have become painless, with the compiler tools and editor telling me what I must fix up.
There may be a special breed of programmers who need no such help, but I have yet to meet them in my 32+y long professional programming career :-)
You still have bugs, you just haven't found them yet. When strict typing is not available you adjust and use other tooling and tests.
Of course with a big, venerable project the balance changes drastically. But an experienced single-person project should be doable with javascript for a long while and may even get to MVP faster by focusing on things that are essential.
I’ve been coding professionally for 39 years. ts seemed very strange to me at first. Only even started using it because I had to do some customization in NetSuite and some third party type and build support in ts made comprehensible the typically-stilted and insane NetSuite js required by SuiteScript.
Now I’m addicted to it. Specifically the lightweight and easy to use _interface_ directive feeds my practice of self-documenting code with good doc and reusable data structures.
That’s the beauty though, isn’t it. Most bugs are trivial. I appreciate typescript getting the trivial bugs right more often than I’d trust myself to do so.
Right? The argument that static/strict typing mostly catches trivial bugs isn't an argument against the value of that language, and yet people seem to think it is!
I have to disagree. Adding the types will increase the number of the characters/lines of code which with a 80-char formatter limit can make functions look almost gibberish at a glance. Do that on a whole codebase and it becomes a mess to look at.
The verbosity of TypeScript types can be attributed to poor choice of syntax and names that became obvious retrospectively.
Still even with TypeScript if types makes your functions ugly, then it shows the complexity of the code. Often it also suggests sensible refactoring that without types would not be apparent.
The only way it can not be noise is if you have no idea what your types are. That's a terrible way to code.
If I'm working on a function foo(bar, baz), I know exactly what bar and baz are before looking at a single line of it, I've been tracing the code through to that function, how could I not know what the data types are at that point?
It's normal not to be aware of the types when encountering code for the first time, or after a long absence.
Your described experience is the rare exception when working with third party vendors, on long lived projects, or with teams that are not trivially small.
So the benefits are only available once or twice, but the code is going to be verbose forever.
If I were to come up with environment where it is guaranteed to be a net positive it would be a company with lack of boundaries between teams and a lot of churn.
Essentially TS averages out engineers in your team. You're going to be slower, but more predictable. Your 10x engineers will become 5x, but 1x will become 2x.
> Essentially TS averages out engineers in your team. You're going to be slower, but more predictable. Your 10x engineers will become 5x, but 1x will become 2x.
It’s the other way around. For more experienced engineers TypeScript is a huge productivity multiplier: you can fly around a large code base with ease, making changes all over.
More experienced engineers also probably have some history with other statically types languages (perhaps Java, Swift or C++) so the concept of static typing is likely to be familiar to them.
For less experienced engineers, there are more drawbacks such as 1) additional language complexity and 2) possible unfamiliarity with static typing. Although TypeScript still a productivity boost because they are likely to have a higher error rate.
See, I've heard it multiple times. And yet nobody is able to provide evidence. Surely if effects are so huge it's should be easy to prove. We're commenting on the article that says TS is 10 years old. Where is the hard evidence?
When you ask about real examples of productivity boost, TS folks shift focus to bugs (why not talk about bugs in the first place?). When you ask about critical bugs, TS folks shift focus to IDE autocompletion. When you press for evidence, goal post is constantly being moved.
Whenever you see bold claims and no evidence other than personal stories for several years, you should immediately attribute it to placebo (or other biases).
> you can fly around a large code base with ease, making changes all over
Not really, unless you're talking about bloated codebases. Huge codebases are huge due to domain complexity, you can't be making changes all over without extensive testing. What does help is having a modular codebase with good boundaries. If you already have it, TS benefits are tiny. If you don't, you're better off spending time making it modular than switching to TypeScript.
> TypeScript still a productivity boost
And yet if you talk to popular library maintainers in Python/JS community, a lot of them say that types have slowed them down. Some of them still believe it was worth it, because it helps users of those libraries. But again, see how the goalpost is being moved?
TLDR: for productivity, there's basically no measurable impact detected in studies, positive or negative. For security and bugs, the impact is as expected: dynamic languages are as safe as strictly typed languages if they are paired with exhaustive test suites which enforce type safety. You can either write the type information alongside the application code, or write the test suite to enforce it.
Folks who think Dynamic Typing frees them of having to consider types are just fooling themselves; in this thread, even, the argument is that because they know what the types are then stating them is just unnecessary "noise". Which is all well and good, in the here and now, but provides no value when the code is foreign or forgotten; and with just a little additional syntax both the information is available to the programmer and the compiler can perform performance and security analysis with greater ease.
> for productivity, there's basically no measurable impact detected in studies, positive or negative
As I said, controversial at best.
But you're not being completely honest here. Most research is either inconclusive, or says dynamic is more productive. It is nearly impossible to find research that says static typing is more productive.
Which is quite obvious today, Uncle Bob and Steve Yegge were talking about this since 2000s.
I didn't even get to the function body, and I already used multiple levels of my mental stack. Ugh.
And gradual typing is the worst. It is either a terrible idea, or at least a terrible implementation (looking TS/mypy). It combines the worst of two worlds.
The only viable benefit of gradual typing is making dependency hierarchy explicit.
The verbosity is not a meaningful performance impediment. It takes a trivial amount of time to write, and a trivial amount of time to read.
If the a team can distinguish between an average and excellent programmer's performance by the time it takes to read or write type information then I expect the skill and experience breadth within the team was already narrow.
And I have to disagree in turn. Those additional characters and lines of code are documentation, and turn gibberish into not only human-readable clarity about the code you’re reading, but a machine-traversable dependency graph as well.
Character input time is a vanishingly small concern for overall productivity. If it's a concern then you'll probably catch more bugs by slowing down to think.
Typing it is not the problem. It is reading at a glance that becomes much harder since lines oftentimes get split into multilines. I get less of an overview.
I find it catches a lot of design mistakes for me which I would put in the non-trivial bugs category. Many bugs that result from design mistakes are less immediate, like hard to spot edge cases (a better design makes them impossible) or code that lends itself to becoming messier and more bug prone in the future as it's changed/extended (a better design makes this less likely).
I’ve had the opposite experience with complex type systems. They make it harder to evolve out of bad designs. This is particularly true when a lot of inheritance is used.
Interesting. What I've found is that when I come up with some pattern that is hard to type, or I have two parts of a system that need to work together but the interface can't be typed simply or doesn't quite 'fit', it is almost always the case that changing things around until the types do work straightforwardly uncovers a better design.
This is definitely sometimes not easy, and of course it'll get more difficult generally the more code there is or the more complex (usually meaning bad!) the design is, but I'd almost put it the other way round to your perspective - the types are like the signposts in this design evolution and discovery process. They don't make it easier to do the work of refactoring a design, but they do make it much easier to understand when I'm going in a good/bad direction and also when I have succeeded.
You are describing writing code alone. I’m thinking about other peoples code I’ve had to deal with where I didn’t create the types. Not saying I’m great at it, but that you can’t always control the quality of the code you have to work on.
That is true for nominal type systems such as Java’s.
However TypeScript’s structural typing is much more flexible, because it is designed to describe the types of all the existing JavaScript code out there (via the DefinitelyTyped project).
This gives the best of both worlds: the freedom to express the architecture you need, plus the freedom to actually refactor an existing code base into that new architecture without introducing many bugs. Dynamically typed languages give you the former but not the latter.
Exactly, you have to run the code before you realize it’s defective and even then only the defects that execute will get any attention. With TypeScript the defects yell at you before executing the code.
There are other costs. One study I’ve seen found that writing libraries with typed generic code too longer to write, but we’re slightly faster to use. I’ve found that changing peoples badly typed code is worse than changing similar dynamic code.
In short, there is very little empirical data on this, and it’s almost entirely anecdotal. My gut feeling is that if typing were a huge benefit it wouldn’t be so hard to prove.
Avoid things like generics. Keep your types as primitive as possible. Strong typing will covert an excellent developer into an amazing developer but won’t do anything for extremely bad developers. Data types are not a solution to people problems.
With TS you're paying the full price (in verbosity and complexity) but getting 10% of the value. There probably still are projects where it's a good deal, but my bet is they are the minority.
I've written some projects w really complicated data and I personally think it would take 5x as long or push me to almost give up if I didn't have type checking.
Typescript was my stepping stone into the world of Rust.
Even before Deno made it easy, it was straightforward enough to configure a simple tsconfig and just run tsc. Much like cargo, there is a lot to be said for "it just works" tooling - especially for beginners or even new programmers.
It is probably fair to say it is one of the most influential and impactful languages of all time. There's even the future possibility of much of its type syntax being absorbed back into JavaScript: https://github.com/tc39/proposal-type-annotations
Opposite experience. Rust was my stepping stone into "hey, maybe JS will be better with some degree of static typing?" Yes it would. But not the way TypeScript does it though, and there aren't any other viable options, are there?
TypeScript brands itself a "superset" of JavaScript. In practice, it arbitrarily invalidates completely sensible JavaScript idioms.
There (kind of) is another option. It is called Rescript. But to be honest will probably never take off... :( For me it has by far the best type system for a script language. It is based on Ocaml. Its syntax is easier to read than Ocaml and as powerful as Ocaml if not more by aiming at JS world. One great feature you can take a look at to see how simple and powerful the type system is is Pattern Matching / Destructuring [1] and its "switch".
Strictly speaking, it takes exactly one such behavior (that you cannot even disable) for TS to stop being a superset of JS.
>although classes are out of fashion, so this is low impact
Looking at the TSC codebase, so are keyword arguments. The "in" thing is just to write very very long lines of multiple verbosely named positional arguments instead.
That said, "clases are out of fashion" is a complete non-argument. I'm of the functional persuasion, yet I've found that classes are the ony way to write TypeScript that fits on your screen at all.
Especially now that classes are being introduced in JavaScript proper, and of course TypeScript does them only slightly differently (handling of default property values and "definedness" differs).
>On the second, doesn’t this work?: ‘function foo({ bar = 3 }: { bar?: number })’
You also need a `= {}` there, otherwise you'll need to call `foo({})` - it won't let you call `foo()`. This is also in JS though, so a bad example of TS breaking things (`function foo ({ bar })` still won't work though). There are probably better ones that people encounter, work around, and forget about, because nobody's listening anyway. "The code making sense is not important, what's important is helping the user" lol.
Now imagine how the above looks with 5-10 kwargs (because keeping context in the class instance is "out of fashion", so it's either a ton of args per function or a "context" record which is effectively reimplementing classes but with a worse experience), and an aggressive formatter insisting every individual thing has to be in its own line.
Here's another: failing to infer the type of `this.constructor`.
Sure, the constructor signature may change in a subclass (why not disallow incompatible constructor overrides, given incompatible property/method signatures are already disallowed?); then what about static methods accessed via `this.constructor`?
So you end up defining an interface type for the constructor and using `(this.constructor as MyConstructorType).staticMethod` or whatever. Which is just visual noise where the fucking intent of the code was previously clear as day, so clear that TSC should've been able to infer it (yeah the type inference also sucks).
Also, ever seen TS2322? It's my pet now. What it do
All in all, TS really puts the "Java" back in JavaScript, and then some.
EDIT: Also crap like not being able to have a question mark and a default value in positional arguments so you gotta add `|undefined` there. Even the stuff it adds on top of JS is poorly thought out.
> I'm of the functional persuasion, yet I've found that classes are the ony way to write TypeScript that fits on your screen at all.
Huh? I’m of the functional persuasion too, and I use classes in TS too, but for strategic reasons (well defined value objects are easier to reason about than duck typed POJOs, and they perform better too). But I’ve never found them more space-dense than the equivalent function-only code. Often quite the opposite, as so many functions’ return types can be fully inferred. Which of course, this is how you get ~~ants~~ duck typed POJOs, but you can’t have explicit type defs without explicit defining them somewhere, and of course the syntax that collocates the field type and its value is more dense than the syntax which is wholly incompatible with that concept.
> handling of default property values and "definedness" differs
The only difference is that TS provides a fully optional shorthand for assigning both the type and value in constructor arguments. The actual behavior isn’t any different. This:
class Foo {
constructor(readonly bar: number) {}
}
is identical to:
class Foo {
readonly bar: number;
constructor(bar: number) {
this.bar = bar;
}
}
class Foo {
readonly bar: number;
constructor(bar: number) {}
}
is identical to this type error, just caught sooner:
class Foo {
bar;
constructor(bar: number) {}
}
const foo = new Foo();
foo.bar.toFixed(2);
> Sure, the constructor signature may change in a subclass (why not disallow incompatible constructor overrides, given incompatible property/method signatures are already disallowed?)
Because the compatibility is checked on the `super` call which is required both at compile time and runtime, and because many use cases for subclasses are impossible or even invalid without different construction contracts.
I know there are many strong feelings about examples like this being “wrong”, but it’s a common enough inheritance example to illustrate the point:
class Square extends Rectangle {
constructor(/* ? */) {}
}
You cannot satisfy both Square and Rectangle with the same constructor arguments. This of course bolsters the point that this inheritance model is “wrong”, but it’s exactly right according to the domain, and the equivalent functional code to calculate eg area would similarly have to be either polymorphic over different shapes or expect a single shape constructed with different parameters.
> this.constructor
You got this one right, and it’s worse than you describe because of the weird rules for where you can’t have type parameters or explicit `this` types. The workaround is to use a static factory method, but it’s a shitty workaround with a lot of ceremony to do something that TS generally does well: model the types of real world JS code.
> Also crap like not being able to have a question mark and a default value in positional arguments so you gotta add `|undefined` there. Even the stuff it adds on top of JS is poorly thought out.
Ima help you out! Assuming your default satisfies the non-undefined type, you can just skip the union, it’s implied. This:
const foo = (bar: Bar = someBarSatisfyingValue) => {};
>you can’t have explicit type defs without explicit defining them somewhere
Sure, as long as I have to define them once and exactly once. Not always possible, as in the case of simple, garden-variety keyword arguments.
// foo is required, bar has default, baz is optional
type POJO = { foo: number, bar: number, baz?: number }
function myFn ({ foo, bar = 3, baz }: POJO = {}) {
// oh wait...
function myFn ({ foo, bar, baz }: POJO = { bar: 3 }) {
// oh wait...
function myFn ({ foo, bar, baz }: Partial<POJO> = {}) {
bar ??= 3
if (foo === undefined) throw new Error("type safety")
// ...oh.
// maybe?:
function myFn ( foo, bar, baz }: POJO = { foo: undefined as never, bar: 3 }) {
// try :D
Technically the destructuring and the type declaration are completely separate things ofc (that just happen to look about the same because they're isomorphic but that's a watchlist word).
But... it doesn't even try to infer the type of an untyped destructuring - even if it's a local function used only once!
>well defined value objects are easier to reason about than duck typed POJOs, and they perform better too
Long live those!
class BaseValueObject {
constructor (values: Partial<this> // oh wait...
> Assuming your default satisfies the non-undefined type, you can just skip the union, it’s implied
type Foo = { defaultBar?: Bar }
function main (foo: Foo, bar?: Bar = foo.defaultBar) {
// oh wait... parameter can't have question mark an initializer
function main (foo: Foo, bar?: Bar|undefined = foo.defaultBar) {
// this works but is silly and scaryish
>The only difference is that TS provides a fully optional shorthand for assigning both the type and value in constructor arguments. The actual behavior isn’t any different
That's what a sane person would assume, no? Well, allow me to disappoint you (like that ever needs permission):
$ node
> Object.getOwnPropertyNames(new class Foo { a })
[ 'a' ]
$ npx ts-node
> Object.getOwnPropertyNames(new class Foo { a: any })
[]
Probably because it compiles them to a pre-standard, ES5-compatible class implementation based on good ol' `Foo.prototype`. And since they've already handled them one way, they can't become spec-compliant without breaking backwards compatibility.
The other place where this shines through particularly egregiously is the support of ESM static import/export. Everybody's build tools been compiling that back down to CJS so hard that Node.js 16+ introduced intentional incompatibilities between CJS and ESM modes just to get people to finally switch to the standards-compliant module system. So you end up in a situation where the library is written in TypeScript with ESM syntax but the only available browser build is a CJS blob which completely defeats the main touted benefit of static imports/exports, namely dead code elimination...
So you decide what the hell, let's switch TSC to ESM and moduleResolution node16, and end up having to use something like https://github.com/antongolub/tsc-esm-fix because the only allowed fix for TSC doing the wrong thing is at the completely wrong level - https://www.typescriptlang.org/docs/handbook/esm-node.html - if you don't see what's wrong with that, you're one of today's lucky 10000...
> Not always possible, as in the case of simple, garden-variety keyword arguments.
// foo is required, bar has default, baz is optional
All of your examples are correctly identified by TS as type errors, because they all have a default argument which will never bind `foo`. True in JS as well as TS. Consider the untyped code, with some access to a required `foo`:
function myFn ({ foo, bar = 3, baz } = {}) {
return bar + (baz ?? foo);
}
myFn(); // NaN
Your function shouldn’t supply a default argument, because if the foo property is required the object containing it has to be too. It should instead supply defaults to the properties in it. This is closer to what you seem to want:
function myFn (pojo: POJO) {
const { foo, bar = 3, baz } = pojo;
// foo is required, bar has default, baz is optional
return bar + (baz ?? foo);
}
myFn(); // Compile error
myFn({}); // Compile error
myFn({ foo: 2 }); // 5
myFn({ foo: 2, bar: 4 }); // 6
myFn({ foo: 2, bar: 4, baz: 6 }); // 10
myFn({ foo: 2, baz: 6 }); // 9
> But... it doesn't even try to infer the type of an untyped destructuring - even if it's a local function used only once!
Yes, it does, if the thing you’re destructuring is typed. It doesn’t infer from usage, and while that sounds nice and some languages have it, it's fairly uncommon.
> oh wait... parameter can't have question mark an initializer
Yeah. It can’t. But if you applied what I suggested, it compiles and has the type you expect without adding undefined:
type Foo = { defaultBar?: Bar }
function main (foo: Foo, bar: Bar = foo.defaultBar) {}
type Main = typeof Main; // function main (foo: Foo, bar?: Bar | undefined) {}
> Probably because it compiles them to a pre-standard, ES5-compatible class implementation based on good ol' `Foo.prototype`. And since they've already handled them one way, they can't become spec-compliant without breaking backwards compatibility.
You’re partly right. I’m on mobile so I can’t dig into the failure but type checker seems to be crashing or getting stuck due to the confusing syntax. If you make it more clear by putting parentheses around the class expression, you still won’t get any compiler errors because it’s constructed with no arguments, and a is implicitly assigned undefined which satisfies any. If you then give the a property a non-any/unknown type you’ll get a compile error because a wasn’t assigned.
It’s weird that even newer compile targets don’t get an assigned a: undefined at runtime, and definitely qualifies as a compiler bug (you should file it! I’ll add what I’ve learned!). It certainly does if you actually assign anything to a during construction.
> Everybody's build tools been compiling that back down to CJS so hard that Node.js 16+ introduced intentional incompatibilities between CJS and ESM modes just to get people to finally switch to the standards-compliant module system.
This is factually false. CJS is fundamentally incompatible with ESM, and has been since day 1. They shipped it incompatible from at least Node 12 because there’s no way to make it compatible. ESM is fundamentally async, and CJS is fundamentally blocking. ESM imports are “live”, CJS are static values at the time you call require. They have fundamentally different module resolution algorithms. All of this has been documented in Node also since at least v12, and has been spec compliant (notwithstanding since fixed bugs) the whole time.
There are definitely valid gripes about how TS has supported ESM though, particularly in terms of file extensions. Thankfully they’re actively working to address that now.
>Thankfully they’re actively working to address that now.
Where? The response I saw on GitHub issues (to the few people who considered it worthwhile to be vocal about the issue) was literally (well, paraphrased): "yeah we did this wrong but we're sticking to it anyway" (because of MS-internal org inertia I assume, something that the TS devs surely have to account for but it's hidden from us as an "open source" downstream)
I’m going to bed but don’t want to leave this unanswered before I lose track of it. They’re working on it as part of the next major release and you can see that in the roadmap they’ve posted for it.
... as comments. Which superficially resemble the syntax of type hints but do nothing. Which has to be one of the worst language design decisions of all time.
I'd disagree that its a poor decision, some help is better than no help when reading code - and if it's inline then it's intrinsically meaningful.
By no means is it perfect, the language doesn't enforce the type rules, but that the developer has the option is far better than not. As far as I can tell the proposal would pretty similar to Python; one can misleadingly or accidentally misuse type hints like the following:
But when done right, it gives you a leg up when you come back to read your own code or scan over someone else's. When you're reading over other's code in group programming assignments, comments make a world of difference (a niche example).
It is a choice, but its use in code might just inspire someone to investigate further - my own introduction to types in programming came when I wondered what these oddly placed colons and arrows were in some Python I came across. If they do add these annotations to JavaScript, some future programmer browsing the source of a web page may well just stumble upon types and have a whole new world of theory opened up to them - I'm all for it.
That's fine for atomic types like string or int, but typescript goes a lot deeper than that with powerful generics. For example, I'm working on a library of functional data wrappers. This would be useless without Typescript and impossible to type without generics.
There's also no way to share and reuse types in comments. Do you copy paste comments to "type" things? What about complex objects? What happens if the comments get out of sync? How do you test them?
I used to be a typescript skeptic too, but much of that skepticism tends to come from drastically underestimating the power of the TS type system.
I just want to note that it seems incorrect to call this a superficial resemblance in terms of syntax. I went and read through the whole thing, and the syntax for the type hints seems to be as close to 100% TypeScript as possible, which is to say, it is very close. There may be some features missing that TS supports, but the bulk of it is there, and what's there is basically Typescript, which is the best thing that could possibly be accomplished here.
Typescript has been doing a fantastic job, and this proposal is continuing in that same vein, truly absorbing as much of that as possible back into JS!
Kudos to everyone involved, great effort!
And re "[the type hints] do nothing":
The section at the end of the proposal clearly explains why that must necessarily be the case: Evolutions of JS must not break the web (especially) for the users.
Quoting: "TypeScript's model -- which has been highly successful for JS developers -- is around non-local, best-effort checks. (...) Additionally, defining a type system to run directly in the browser means that improved type analyses would become breaking changes for the users of JavaScript applications, rather than for developers. This would violate goals around web compatibility (i.e. "don't break the web"), so type system innovation would become near-impossible. Allowing other type systems to analyze code separately provides developers with choice, innovation, and freedom for developers to opt-out of checking at any time."
So this proposal provides the best possible path forward for JS, based on what folks are voting for with their feet by using TS: Making JS compatible with TS-style type hints that can be used by external tools (i.e. not the JS engines executing the code at runtime) to validate the code in a best-effort manner while the developer is looking at it, while not changing JS runtime semantics and thus never breaking the code while the user is running it.
I don't see the problem with this design decision.
I'm not the biggest fan of static type checking (especially in a language like Javascript, where it isn't used for safety guarantees), but it has its uses.
The "as comments" part of it isn't about syntax. They really mean no runtime overhead/behavior. (I don't think they should have put it that way -- it's just going to cause confusion.)
Is there really any question that "no runtime overhead" needs to be an option? (I think any proposal that didn't include this would be DOA.)
It also seems clear to me it needs to be the default option, for multiple reasons (language changes should be backwards compatible as much as possible, it should be easy to adopt new language features incrementally, type usage is an application-level concern.
Note: there's nothing in this proposal that prevents run-time enforcement. Good design limits scope but keeps your options open.
I would prefer if they actually had a purpose in the language in which they were used (JS.) Add optional type hinting to JS itself, if it's that important, don't add pretend type hinting to it because typescript users don't want to have to use their compilers anymore.
I know a lot of people like it but it just seems like an ugly hack to me, extra syntax for the sake of an abritrary third party tool. I know I'm a dinosaur. I want to go back to the days of JQuery modules and FTP. Feh.
Is there any news about the status of the type-annotations-as-comments proposal?
The README has said "Details will change in the coming days" since March 31. I know TCs are supposed to move deliberately, but this is such a neat idea I'm impatient.
I feel like I am the only one in the world not liking typescript. It is not that I don't like types, it is what those types do to the readability of the codebase in terms of verbosity. My original programming language was Java but I switched to Node.js because I liked the simplicity of the code written in it. Nowadays every Javascript project seems worse than a Java project in terms of verbosity.
My main gripe with projects becoming verbose is that the code becomes hard to understand at a glance. Functions that usually could be 10 lines are now 15 lines becomes of the formatter and each line looks more gibberish because everything has a type. You have to manually filter out that noise. The best way I can describe it is that entropy is increased in order to get type safety.
I'm also in the not-liking typescript camp. I was an early adopter (I think my first version was 1.7 or 1.8) and it put me off forever. What increased my productivity the most was not languages, linters, build tools, compilers, transpilers, etc... it was a good IDE. Now I just write plain Javascript with JSdoc and I don't need to update my toolchain every week.
I maintain several JS+JSDoc projects for work, and several TS personal projects. You can certainly get a very similar IDE experience with plain JSDoc, but the tradeoffs are pretty severe. Certain aspects of the type system have no JSDoc equivalent (assigning a type to a class definition), or worse have conflicting equivalents (eg enum, which I know everyone hates in TS but the JSDoc version is worse), or require cramming TypeScript syntax onto a single line in (eg conditional types).
Most plain JS projects use some build tooling even without compiling TS, my work projects are no exception. When I started the job, the build tools (Rollup and Terser) worked just fine and had been in place for years. A short while later I made the case to switch to ESBuild to improve iteration speed, which took a couple hours maybe? It’s been almost totally untouched since. And if I had more time, I’d be able to gradually convert the code to TS without any additional tooling effort.
I get that tooling pain in the JS/TS ecosystem is basically a running joke at this point, but no one is forcing you or anyone else to update anything. Plain old tsc still works if you want to keep it simple (and it’s what I recommend to anyone who wants to try out TS and feels confused or intimidated by the whole Tooling Thing).
To each their own of course, but living on both sides of the fence… the JSDoc-types side isn’t nearly so green as it can sometimes sound.
Here’s hoping the types-as-comments proposal keeps gaining traction. I’ll still use a build tool to strip types out for prod, but I think the no-tooling/JSDoc-types crowd would benefit a lot from a syntax much closer to TypeScript too.
You're not alone, and I suspect TS crowd is still a vocal minority.
But TS is very viral by nature. You can only go in one direction (JS -> TS). TS promises "type safety", which is mostly a lie, but sounds really good. It also promises to be purely additive, so choosing it seems like a no-brainer. TS is a next step in a JS-engineer career, once you learn it it's hard to admit that it was a waste of time. Especially when there's nothing but praise around, humans are conformist by nature.
Being viral doesn't make it good. But it means that it will keep spreading, until community gets a TypeScript fatigue.
I agree that in your example it is not bad. Throughout that codebase it is clear though that they don't have a formatter that enforces a char-limit per line. It is when you combine those things it gets messy. Prettier uses a default of 80.
Looking at the code there is an awful lot of 'null's returned which I find hard to work with but that is a different topic.
>TypeScript never set out to build a separate, distinct, and prescriptive language. Instead, TypeScript had to be descriptive.
Sadly, the majority of people writing Typescript rarely care about descriptive and readable code these days. Somehow the focus shifted from using types to better understand complex systems through the code itself, to writing whatever polymorphic union type abomination that yields the best IntelliSense auto-completion in VSCode.
The polymorphic union type abominations are precisely because TypeScript is insistent on being a thin layer on top of JS.
In particular there is no built-in ADT syntax, but the type system does support ADTs using the aforementioned polymorphic union abominations, so it is possible yet tedious to write them.
They are in a tricky situation because TC39 really does not care about TypeScript at all when introducing new features into JS. So TypeScript is doing its best to avoid breaking future compatibility with a standard they have no control over. Sometimes this makes the language worse, but they are playing the long game.
I've had a very different experience than yours with the Typescript codebases I've encountered, which were mostly well designed.
I have no doubt codebases with polymorphic union type abominations exist, but I wouldn't say they are the majority, far from it in my experience.
It also seems to me those are a case of "problem lies between chair and keyboard", not a fault of the language. Bad code can be written in any language.
> I've had a very different experience than yours with the Typescript codebases I've encountered, which were mostly well designed.
Agreed. And in fact, the founders of a project having explicitly chosen TypeScript is often in and of itself an indicator that the team cares about code quality and writing style.
As you say, you can write poorly in any language. But people who care about quality choose TypeScript.
I agree, but I would like to say that this is more due to the limitations of TypeScript (and JavaScript) and the need for developers to work around those limits, rather than anything else.
TypeScript is great, but it is also still very far from being complete in my opinion.
I used to do mostly Java and a bit of TypeScript, changed teams and now do mostly TypeScript and a bit of Java, and this unfinished feeling is striking, even for relatively basic things (enums).
I think the modern compiler options / TypeScript rules such as "hey this variable can be undefined but you didn't specify it", "hey, it can also be null, don't forget that" are commandable, but just don't work well for the web. I know it's configurable, but it just makes for a bad experience.
Still, love TypeScript. I wouldn't do any JavaScript project anymore, installing the TS compiler is always my first step.
I‘m not well versed in type theory and some of the types libraries require, especially in the React ecosystem, really make my brain hurt. And sometimes it seems unnaturally hard to figure out what is even expected to type something correctly.
The whole reason Microsoft made TypeScript was so that Intellisense would work. Then that became part of the religion and elaborated on with millions of dollars of marketing.
This. Probably every TS codebase I ever worked in had zero to none function- or inline docs with people always claiming "documented by TS". I call that BS, and you can see that in their projects. "Typescript codebase" became the equivalent of "generic low quality codebase" imo.
I'll never understand why people need a programming language inside a programming language and refuse simplicity (i.e. using jsdocs, which has perfect IntelliSense support).
I tried jsdoc + JavaScript for a while to minimise the distance between me and the browser, but in the end TypeScript was just the easier choice. Less documentation to type, and the checks are better. I found that using TypeScript basically like JavaScript + jsdoc delivers the best value. I avoid any advanced TypeScript features as TypeScript is just not a proper statically typed language.
However I tend to only use /** a comment */ before a type definition or argument which will add the comment along with the type in autocomplete.
I get the feeling you've stumbled more on the debate about commenting code/inline docs vs code that self documents. My general impression of Javascript developers is that they are comfortable adding comments whereas Typescript people are not so comfortable for some reason. Maybe because a lot of them are coming from Java and C# where writing clean code™ is more preferable to adding comments.
Personally I think there's a middle ground. I've met some Javascript developers insisting on adding a comment before each statement and I've met some java developers having a no comment policy in their codebase (so that pull requests wont merge if there are comments).
This is entirely applicable to JavaScript codebases, except that at least in typescript you have less reverse engineering work to do.
If you want to have documented codebases, work with people who document their code. You’ll find that typescript doesn’t always mean undocumented, and incidentally you’ll find that documented doesn’t mean high quality.
Most high quality codebases in the JS ecosystem after 2016 ish are in typescript.
Thank you, Anders Hejlsberg. As a newbie in front-end development, I am grateful to TypeScript (and its integration with VSCode) for taming the dynamic beast and keeping me and my codebases sane. The pragmatic design and composable nature of TS help it fit easily within the JS ecosystem, and the productivity boon helps it survive in that tumultuous landscape.
In my opinion the most important factor was that it is a superset of JS and any valid Javascript code is/was valid TS code.
That allowed for gradual adoption and you didn't have to risk going all-in into new technology.
Also the `any` type [1]. It makes porting an existing project ridiculously easy: just type everything as `any`, and it also gives freedom to the developer to think in code when developing, trusting that they (will) know what they are doing, instead of screaming at the smallest mis-type with annoying bright red squiggly lines.
It is so liberating to type a variable as `any` when sketching new code, figuring things out, that I would venture to say that any language not having the `any` type actually actively hates the developer.
And then you have to contribute to a codebase with restrictive ESLint (bleurgh!) configuration, so you can't even try out whether your code works because the tooling disallows you from compiling it as long as it contains "any", and the other devs are like "but muh best practices". So not only you gotta work around TS, you gotta do it invisibly. How did people even live before VSCode's type hint popups covered up the previous line?
No, cringing at some "helpful" popup appearing right over the previous line of code. If anything, they could've made it appear under the current line; code's still written top to bottom so it's less likely for "suggested relevant info" to obscure the actual relevant info.
TS is emphatically not a superset of JS. It rejects perfectly valid JS leaving you no recourse other than design your whole architecture around what TypeScript allows. Which is what Microsoft wants of course.
You can directly import any valid JS module using TypeScript if you have “allowJS”: true in the compiler options of your tsconfig.json file. The comparison to FFI doesn’t make any sense. FFI requires compiling a special library that explicitly exports the C types. This is different than TS. Using TS, you can import any valid JS module without any special preparation. Also, you can’t import all compiled languages into each other using FFI. You can only import and export C-based types. There is no way to export a Go struct for consumption via FFI, for instance. All valid JS modules will work with TypeScript.
$ echo "export class Foo { a = 1 }" > test.mjs
$ npx ts-node
> import('./test.mjs').then(console.log)
error TS7016: Could not find a declaration file for module './test.mjs'. '/home/user/Lab/test.mjs' implicitly has an 'any' type.
$ npx ts-node -O '{"allowJs":true}'
> import('./test.mjs').then(console.log)
Promise { <pending > }
Error [ERR_REQUIRE_ESM]: require() of ES Module /home/user/Lab/test.mjs not supported.
$ echo "module.exports.Foo = class Foo { a = 1 }" > test.cjs
$ npx ts-node -O '{"allowJs":true}'
> const { Foo } = require('./test.cjs')
> Foo
[class Foo]
> new Foo
Foo { a = 1 }
> function bar (foo: Foo) {}
TS2749: 'Foo' refers to a value, but is being used as a type here. Did you mean 'typeof Foo'?
No I did not, TypeScript, and that's about as much as it seems to interoperate. I guess in some cases practical migration might be viable - but as for the general case, the out-of-the-box experience seems to speak otherwise.
It's not even viable to write in JS and manually write a DTS - no way to check em against each other. Except idk library in JS, test suite in TS anyway. So not a lot of lateral movement possible in practice.
I'm not even talking about the module support. (It just seems to have the default support of TypeScript.)
Yes, it imports the module. No, it doesn't do even basic type inference (knowing that Foo is a class and consequently allowing it to be used as a type name) - which it would, if it was a superset of JS. Instead it seems to import everything as "any", which is... a start, I guess?
I tried using Flow at some point when TypeScript was much newer and wasn't the obvious choice for typed Javascript yet. I couldn't get it to work properly. I probably could have with more effort, but I didn't feel like I would be able to keep it running well and debug issues if they would arise.
Well the fact that it was available solely on Windows for so long was its doom.
Actually I thought of using C# a few months ago, and I was like "Oh wait, does it even work on other platforms than Windows now?". Sure I'm not a C# developer, so I'm not up to date on C# news, but that's an issue if your goal is to drive adoption. Everyone that has heard of C# should know that it's now cross-platform.
Some things still seem to be Windows specific. If I want offline documentation (of the sort one can find at /usr/share/javadoc/java or /usr/share/doc/rust/html by installing the correct packages), every place I look tell me how to enable offline help in Visual Studio (for instance, https://learn.microsoft.com/en-us/teamblog/offline-book-refr...). Someone here told me last time that there's a way to download whole sections of the MSDN documentation as PDF, which helps, but it's not the same thing.
Off-line documentation being kind of a pain sounds like a very minor thing.
Other than that, and other than the obviously Windows-specific GUI and system management libraries, the rest of C#/.NET is pretty much fully multi-platform, maybe except a few obscure things.
“My field” is a 20+ year MS ecosystem developer from C++/MFC, VB6, .Net Compact framework, .Net Framework, .Net Core, etc.
As I said, very few new companies that aren’t “legacy” shops are choosing .Net.
In my current job in cloud consulting at $BigTech, almost all of the .Net projects that come through are older companies compared to newer companies that are using non compiled languages.
These days I write in almost any language they prefer among the popular languages.
“My field” is a 20+ year MS ecosystem developer from
C++/MFC, VB6, .Net Compact framework, .Net Framework,
.Net Core, etc.
Right, and in that field you might be correct (I don't know but I'll take your word for it). It is however not the entire software industry.
As I said, very few new companies that aren’t “legacy” shops are
choosing .Net.
I'm a game developer and the most popular game engine (Unity) uses c#. The newest up and comer (Godot) uses their own scripting language or c# as the most popular options. I've also worked in Enterprise before and c# and java were the popular choices there and as far as I know from friends still are (especially when servicing the government)
You realize you are echoing exactly what I’m saying? Governments are the definition of “legacy” Windows shops. The whole thing I’ve been saying is that new initiatives outside of historical Windows shop are not adopting .Net even though it’s been cross platform and open source since 2016. The new startups and newer companies that avoiding Windows like the plague are also avoiding .Net.
In the US, the companies that pay top dollar - the large tech companies and well funded VC back companies are not adopting tech companies.
If that's what you meant ("c# is unpopular among the best paying tech companies") I can accept that. But what you originally wrote that I disagree with is:
That unfortunately hasn’t helped C# become more popular outside of
Microsoft legacy shops even when it did go cross platform and open source.
Where I work is very much not a microsoft legacy shop (a game dev startup) & we use c#. This is hardly unusual.
When talking about compensation, it might as well be an “enterprise shop”
Just in case you don’t read the link above, that’s not meant to be derisive. It’s just a pure statement of fact.
There are plenty of “startups” that locate in major cities in the US outside of tech hubs, find a bunch of MS developers to write the next CRUD SaaS app and pay enterprise dev wages. I should know, I spent over two decades working for them
I understand what you meant, i just disagree with your phrasing of it. You can simply call it the "best compensated/all the rest" divide. But other than that game development is not at all like Enterprise so I don't find yours a good name for this supposed dichotomy.
I think you would get a lot less pushback by simply using more precise terminology- even though for you it may all be the same, for people working outside of FAANG-esque companies it isn't.
In the US though, compensation for software developers is very bi-modal. You have the “enterprise shops” that start off around $80K and max out in the mid $100s and the “tech companies” that start out in the mid $100s and end up in the $350K+ range.
Most of the 2.7 million developers in the US are on the “enterprise dev” side. If you have a choice, you want to be on the “tech company” side if you care about your compensation.
For context: because of path dependencies and bad career choices, I spent most of my career from 1996 - 2020 on the “enterprise dev” side and only got into $BigTech by pivoting to cloud consulting (enterprise dev + cloud + a shit ton of yaml/HCL PowerPoint slides and diagrams)
Before anyone “well actually”’s me with numbers they are directionally correct on the enterprise dev side in most major cities.
C# and ASP.NET Core feels like the default stack in Western Europe, even among startups. You have to look harder to find companies using Node.js, Rails, Django etc.
The “startups” in the US are even bimodal. You have the ones that set up shop outside of the west coast in big cities that hire “enterprise devs” and you have the well funded ones that try to compete with
big tech salary compensation.
But writing Typescript in Typescript allowed the language-designers to dogfood the developer-experience of being a Typescript-developer.
Just like MS eventually did with C# too. After the C#-compiler was reimplemented in C# (Roslyn), that’s when the language was truly allowed to develop. And it also made it much easier to make C# a truly cross-platform language.
When the language-designers don’t have to use their own language, they have objectively much less motivation to improve upon the language or it’s tooling than if they do.
The Flow-team didn’t use Flow to write Flow and that shows in its lack of development, its lack of tooling, its lack of contributors and ultimately in its lack of adoption.
My project is small but TS takes 4-5 seconds to compile it from scratch on each run.
The main speed impact is in developer productivity though. If something ain't working right, now I first gotta fix the types before I can see if I've fixed the actual logic.
I imagine if my codebase was more "OOP-y" (i.e. if I replaced every layer of my domain model with 3 layers of dependency injection, turning the whole thing into an inscrutable ball of spaghetti like the cartel wants me to) I could probably iterate without breaking the types.
But for any sane developer having made the mistake to touch TypeScript, the capability to strip the types and run the code with broken types, is essential.
"But then why have types at all?" Exactly. They're a crutch for people who want their IDE to understand the code instead of them.
It's always fascinating how different people approach writing code. I've worked with some very talented devs who preferred dynamic languages because they liked what I call a "smash face on keyboard" style of coding. They wanted to be able to make a change, run the code, make a change, run the code, often repeating that dozens of times before they got it right. I, and I think many people who prefer statically typed languages prefer a more methodical style of programming where we spend more time reasoning about the types and how data flows through the app. I might only run the code every five or ten minutes, but often if it builds it's right the first time. If the types are wrong, the code is wrong, and I've no interest in running code that's wrong.
Is one side or the other right or better? I dunno, I'm not qualified to answer.
TypeScript doesn't entirely prevent you from doing that, even if you have to stick in a few "any" keywords here and there and ignore whatever linter warnings you might get.
I'm happy for individual devs to work like that if that's what works best for them to get their code going - but I'm certainly not happy for a shared codebase for a large complex project with dozens of devs on it to operate the same way.
For me the best experience during development is to build ts to js first with something fast like esbuild and check types in parallel without preventing the output in any way from running in the browser.
A perfect scenario for me is having all errors in the project caught by vscode. At the moment vscode only checks files that are currently open. I think type checking should only be done when building to some sort of production or realtime in your editor. Templates like "create react app" can report runtime errors but it's somewhat strange that it also report typescript errors. (although understandable given that it wants to be tooling agnostic)
One potential major upside with adding optional type hints to javascript is the ability to run typescript files directly in the browser without needing to strip types in a build step. Advanced type checking could be done the same way we use linters like eslint.
>At the moment vscode only checks files that are currently open
While TSC checks everything by default - even files that are not even part of the dependency graph. That's how great the "great tooling" is in reality.
>I think type checking should only be done when building to some sort of production or realtime in your editor.
Agreed, that's the lest awful option. The problem is that they had 10 years to think of it, and didn't.
>Templates like "create react app"
...are a big part of the problem. "But I don't want to spend a whole day just setting up a project!" You wouldn't have to, if you didn't drink the React kool-aid in the first place.
> But for any sane developer having made the mistake to touch TypeScript, the capability to strip the types and run the code with broken types, is essential.
You’re free to hold that opinion, but I think you see (based on downvoting) that your definition of a “sane” developer is not as universal as you may have thought.
Personally I almost never see any value in running known broken code.
If I need to run/test a subset of my code without a complete working system, I have unit-tests for that purpose.
>your definition of a “sane” developer is not as universal as you may have thought
Where I'm from, sanity has historically been the minority opinion, so we don't really have a word for this, but I think the English one is... "gaslighting"? "TS is a superset of JS", "there are 4/5 lights", "this line is longer/shorter", etc. (Look those up if you haven't, Microsoft marketers surely have.)
What I'm saying is, I am well aware that I represent a minority, and guess what, getting downvoted (not nearly as much as expected) still beats keeping silent about the things that have been fucking with my head for the past, what, year and a half? Considering I've been writing Node for barely 6, the fact that every year the number of software developers doubles [cit needed] probably has a lot to do with it.
No matter if we're talking about a very simple or a very complex system: if there are a lot of subtle inconsistencies in it, and you have to gradually learn your way around them, your ability to keep a consistent mental model of it in your head, and reason about what you're doing, will be impaired: -1 sanity.
Ironically this is the same as people's gripes about the original JS type system. IMHO `==` and `===` should've been the other way around, and 1000 ships wouldn't've been launched. This unwieldy choice of notation (implicit type coercion on `==`) made it just enough subtly different for people expecting C-like strict equality, to create a feeling of confusion and distress.
But since the underlying design principle was not clarified (in browsers all input comes from text boxes, as strings, anyway; and the original "source of truth" was only the HTML file, where element attributes are also strings; so in some limited sense it makes sense to have convert-from-string by default), we get all these (generations of) people with the impression that JS is a "low sanity" language... and that it needs, of all possible features, even more of a type system.
>Personally I almost never see any value in running known broken code.
That's completely subjective. Personally, I don't know how to fix a piece of code without being able to see with my own eyes how/where/why it breaks, but surely that's just my deficiency and everyone else is just telepathic. Boo, shame on me! (I'm also the guy with the months-long pull request because guess what, TypeScript did not make it easier for me to see what I'm doing wrong, just looped me into a waves of type refactors, sometimes causing me to break working code to get dat "spellcheck" out of everyone's faces. I do semver, but most libs on NPM don't ...)
So, in my experience, TypeScript prevents me from running known good code way more often than it prevents me from running known/unknown broken code. As for what value you see in either, sure - that's a matter of style.
Rust was mentioned elsewhere in the thread. It's great to work in a language where "compiles"="works". But TS is not a static language, where types actually matter; it's a restrictive "sanity checker" overlay on top of a dynamic language. And not one that's good enough for me to sacrifice the expressivity that JavaScript allows.
Maybe it's good enough for many other people who never came to rely on JavaScript's flexibility that much in the first place. Thoughts and prayers to 'em. Each one has to make the choice which is the right tool for a job - and when there's an incipient monoculture trying to make that choice for me, I'm bound to make a ruckus.
> If I need to run/test a subset of my code without a complete working system, I have unit-tests for that purpose.
Amen to that! Thing is... you see how elsewhere in the thread, it's mentioned that people use TS annotations as an excuse not to write documentation? Well, in my purely anecdotal personal experience, they also use it for an excuse not to write unit tests! And considering what unit testing in JS looks like... well, no surprise there, either. "Ubiquitous JS" is supposed to be accessible. Instead, we have a number of onramps to it covered with rusty razor blades,
TypeScript is not without its good parts, but overall my journey with it has been one of frustration and, as I'm sure you can see from miles a way, a whoooooole lot of mismatched expectations. Like, ESM working...
Hence the exaggerations, and generally the lots of writing. I like JS/Node, I consider them simple I've been round the block just enough to see it evolve a bit, I gotta adapt to what others are doing (TS) because otherwise good luck getting help, right? So this is my honest perspective about that and I'm here to represent it because it's gotta amount to at least as much as the usual "nah bruh everything ok... u ok?"
> Where I'm from, sanity has historically been the minority opinion
You are conflating santity for your subjective opinion. I guess your post might have been received better if you showed more awereness of that.
> That's completely subjective.
Yes. Which is why i prefixed "Personally I...". I'm clear about this being my opinion, not an universal truth.
> Personally, I don't know how to fix a piece of code without being able to see with my own eyes how/where/why it breaks
If the compiler tells me I'm passing a string to a function expecting a number, I find that error pretty easy to grasp, and I don't need to run the code to know how to proceed from that error-message.
> But TS is not a static language, where types actually matter; it's a restrictive "sanity checker" overlay on top of a dynamic language.
Sure. But if you (and that's up to you!) decide to write your code using types and allow TypeScript to check them, at least you know that your code in isolation is unaffected by whatever dynamic runtime it will end up being run on.
> And not one that's good enough for me to sacrifice the expressivity that JavaScript allows.
So you prefer to go full iron, and rather trust the correctness of your own code, than having a compiler check it for you.
That's your choice, ofcourse, but if that's how you do things, you cannot come afterwards and claim that TypeScript is a bad language based on that. It's a bad match for you, and how you would rather manually be in control of everything.
But just tells us about how you like things, it says nothing about the language itself.
> we get all these (generations of) people with the impression that JS is a "low sanity" language...
Javascript as a language has lots of quirks. Lots of quirks which are unexpected to most developers not coming from Javascript. That is to say, Javascript behaves differently than most other programming-languages.
I'm not saying it's wrong or "not sane", but programmers coming from other languages will make assumptions and those assumptions will be wrong for "plain" Javascript, leading to buggy code.
Typescript embraces the modern parts of Javascript, and makes them easy to work with, and makes your code easier to prove correct than plain Javascript would. That's why people like it. That's why people use it.
> and that it needs, of all possible features, even more of a type system.
This is not specific to JS. I think any language used to build bigger systems benefits from having a type-system to ensure the components interacting are always interacting in a known good way.
To me it seems like you're one of those people who prefers to work with all the quirks that Javascript provides and considers those to be good things, even though they are (much) harder to formally verify.
Where Typescript tries to force you to make formal declarations for your code, you find those limiting or getting in your way. Great. Be a superstar if you like.
Me (and many others) on the other hand, are just human, and we like a compiler to tell us about obvious errors in our code without having to discover them at runtime, or even worse, in production.
>I guess your post might have been received better
You assume that I'm primarily optimizing for that.
I optimize for being able to express my opinion in sufficient detail that people actually bother to try to show me things that I don't know, just because they're mildly outraged at my audacity to say, "but some things are very not ok" :)
>Where I'm from...
...they stopped putting people in cages not even for believing outrageous things, but for holding outrageous disbeliefs, about a generation ago. So it's also about maintaining the capability to express dissent.
>I'm clear about this being my opinion, not an universal truth.
God forbid I claim to know any universal truth! Therein lies the way to True Madness. Anything anyone says can at most be their subjective truth - if they can reproduce from memory the things that make them believe what they're saying. Snippet battle was fun
>If the compiler tells me I'm passing a string to a function expecting a number, I find that error pretty easy to grasp, and I don't need to run the code to know how to proceed from that error-message.
And if the compiler happens not to? Back to runtime validation, considering priorities it remains ad-hoc, so the mess gets even unholier.
(where's my authomatic runtime validation layer based on parsing the type definition? holla at me)
>Javascript as a language has lots of quirks. Lots of quirks which are unexpected to most developers not coming from Javascript. That is to say, Javascript behaves differently than most other programming-languages.
>I'm not saying it's wrong or "not sane", but programmers coming from other languages will make assumptions and those assumptions will be wrong for "plain" Javascript, leading to buggy code.
I'm not saying it's wrong for JS to be like that either (I put forward a couple good reasons for the contrary and know a couple more), but I am saying that it is already "not sane" enough - in the sense that the hidden assumptions and unexpected behavior impair one's immediate ability to reason about the codebase.
The transpiler ecosystem doesn't help with that, either - TS, Babel, CLJS or whatever else, if I have to run scripts through a compiler it smells like someone isn't managing complexity right and it sure as hell it ain't every downstream developer who just uses whatever has the most mindshare (so that the quirks are at least googlable). Though I guess you could approach it from that angle too
>rather trust the correctness of your own code, than having a compiler check it for you
Rather verify the correctness of my code by running it, than trust the half-assed third-party static analyzer that's manipulated itself between my editor and runtime, yes
>Where Typescript tries to force you to make formal declarations for your code, you find those limiting or getting in your way.
That's a deficiency of TypeScript. The only option is not to use TypeScript, which is in many cases not an option.
>Me (and many others) on the other hand, are just human, and we like a compiler to tell us about obvious errors in our code without having to discover them at runtime, or even worse, in production.
I'm all in for this! I just wish it worked well enough to justify the drawbacks.
>To me it seems like you're one of those people who prefers to work with all the quirks that Javascript provides and considers those to be good things,
Prefer to know things that will not become invalidated by progress? Who doesn't. I already have to reason about a whole stack of "quirks" from the user's brain to the CPU and back. I would've preferred for Microsoft not to add 2322 more of those out of sheer community goodwill but there you have it.
>But if you (and that's up to you!)
It's up to me on my down time. Rest of my life it's up to the economy lol
>decide to write your code using types and allow TypeScript to check them
"decide to buy into the TypeScript language and ecosystem" because "writing your code using types and allowing TypeScript to check them" by manually writing a .JS + .D.TS is also not a practical option.
>That's why people use it.
My guess is most devs adopted it on their down time to stay relevant, "join us or fade away" hahahaah
>at least you know that your code in isolation is unaffected by whatever dynamic runtime it will end up being run on.
Where did that even come from. You mean like how it's now 4x harder to publish an isomorphic library because the CJS->ESM migrations and TypeScript doing its own things with modules have mangled the packaging surface in further sanity-reducing ways
>Be a superstar if you like.
One does not reach a position of power simply by doing what one wants. You also gotta tell others what not to do :)
You tend to have to write a bunch of code that mostly catches problems you don’t have.
Personally, I’m marginally on the side of using TS even for small 1-2 person projects, but only slightly, and I can understand the case not to.
Well designed projects are no more complex than they need to be, and for many smaller projects that means — at least for web ones — that you have few significant “type barriers” where types are non-trivial and communicate or structure things in a way that is useful. And the type-heavy areas also tend to be where the app is ingesting data from a non-local source — exactly where static type checking is useless. (It’s always extra annoying when you’re fighting the type checker when writing the code that really ensures the data has a valid form.)
At the time, the turning point that finally made me switch from Flow to TS was when it added the JSdoc mode that allows you to have VSCode intellisense without a compile step.
As a C++/C#/Java developer I just couldn't deal with Javascript. Since discovering TS its fast becoming my favorite language. I think I might even use in the back end going forward.
When it came out alongside Dart, I thought Dart to be the better, cleaner solution. After all, Google created V8.
Microsoft and Hejlsberg didn't seem well suited to tackle something so lightweight as JS. The whole .net suite seemed like an overengineeres mess to me and MS wasn't liked after the IE6 incident.
But on recent years I came to like TypeScript. It really is just JS.
I have a rather funny story about Typescript from ~4 years ago. I joined a team that had, amongst other things, an Node-Express.js and React-redux stack, all in vanilla Javascript.
I was a bit younger and bolder then; I lamented with the lead developer about how vanilla JS was probably not a good idea for what was to be a large enterprise platform. The response I got back was probably what you would expect from a senior developer receiving criticism from a rather newly minted developer: Oh i've tried Typescript, but it's just SO VERBOSE. I feel like I can't get ANYTHING done, etc. etc. etc.
I've personally been all in on TS from close to day one - around 2013 when I started some very early web development (hooray for the AngularJS/Angular days! /s). It somewhat scratches my ego and has been rather interesting to see the developer mindset change, particularly that of vanilla JS developers of old. Response like ones I had have gone from often negative, complaining about verbosity and I guess frustration with anything different from what they are used to, to overwhelmingly positive. Old guards join us, or fade away, I suppose.
What do you mean? I'm editing TS perfectly fine in Jetbrains IDEs, and some coworkers use emacs and vim without any issues, still getting all the error highlights, etc.
Is there an editor that doesn't work with Typescript? Even without type error integration into the editor view, you could still run tsc in your shell of choice to manually check if everything is OK.
> I'm editing TS perfectly fine in Jetbrains IDEs, and some coworkers use emacs and vim without any issues, still getting all the error highlights, etc.
That's why you don't have any issues. The error popups aren't the problem. It's the autocomplete and type popups.
The last few years TS has raised an army of shit devs that code in such a way that it's literally impossible to read the code without your IDE popping up multiple boxes to tell you what is happening. They just search every function name, giving zero thought to where anything lives in the directory structure. They type everything like fucking idiots, with layers upon layers of types across 10+ files, all of which you have to open just to find out you're working with an object that has a couple of strings and a number on it. Of course VSCode will just tell you this when you hover over the variable, if you're using it.
Just wait until you get a project where these muppets have a majority and you'll feel the pain.
If you're comfortable with JS, you don't need a book to learn TS.
There are two main resources I would recommend:
- for those who prefer written content, the TypeScript Handbook [1]
- for those who prefer video content, the first couple videos of Jack Herrington's No BS TS series [2]
Other than that, just start using it. My main advice would be that your TS code should look as much like JS code as possible, ie don't explicitly type everything, just rely on inference where possible. Basic generics can be useful occasionally, but most of the "Type Challenge" type stuff is only really useful to library developers.
I wouldn't waste time buying/using a book to learn TS. If you have experience in any statically typed language you should be able to pick up the basics just by a quick peruse of the TS docs.
And then even if you don't I'd still say reading the docs and just browsing existing code assuming you already know JS would get you all the way there.
It actually is. TS is like a force multiplier for shit devs. All the worst code I've ever seen has been in TS and within the last 5 years. The average code quality has fucking plummetted in that time too, it's not just the worst offenders.
I'd argue it's not actually TS that causes this. There are just a lot of unskilled devs.
You mention "within the last 5 years" without also taking into account the absolute explosion in the popularity of becoming a software dev in the last 10 years. So of course theres going to be a lot of bad (and some good) devs. With TS being one of the literal most popular languages in use right now, of course a lot of bad code will be concentrated there, but I wouldn't blame TS itself for it.
You can also blame hiring practices somewhat for it, since a lot of places I've worked for even myself recently hire anybody who can recite a couple ECMA features rather than focusing on good dev practices in general.
Wanted to try it, proceeded to install it with npm and it was incredibly scary, both the amount of stuff it pulled in and various warnings. No thanks, I'll stay with languages with more self-contained compiler.
Yes, it also comes delivered as several near-identical builds for different contexts, each of which is nearly non-extensible. Have you seen the kind of monkey patching Volar (Vue tooling) does to enable type checking of TypeScript embedded in Vue templates? Meanwhile, native JSX support lol
Well TS does have its complexities, but it is somewhat surprising to see the complaint being that supporting typechecking for something that is not TS at all (and something TS authors know nothing about) is complex.
It is amazing that the whole langserver stacking that volar does is possible at all - I don't believe there is any equivalent prior art that worked as well as it does. Despite all the complexity the end user experience is pretty good. If someone had mentioned to me this kind of cross language type-checking can work as nicely before I used volar I would have been super skeptical.
Sure it has its caveats. But it is also completely possible to use vue in pure typescript.
I don't fault them for having native jsx support, React was just too popular in the target user base. I do hope that someday js+ts gets kotlin style builders, but until I'll gladly use volar.
JSX is also not TS at all, yet it gets preferential treatment (TSX). Unlike JSX/TSX, the TypeScript used by Vue is just TypeScript - wrapped in a tag next to some other stuff, so you know where the part you need to type check starts and where it ends. All TS needs to do to support this, on a basic level, is ignore the non-TS parts, i.e. let you turn each non-TS line into a comment.
But no, even adding supported extensions to the compiler, that's right, not even formats, just making TS recognize a new extension as a file that it can load code from, is a no-go; a pre-load hook is the simplest thing to add, had they not made it explicitly, intentionally non-extensible.
I didn't even have time to get into the "stacking of language servers" (something that can and should be as simple as a pipe, and of course Microsoft has all the reasons to make it the opposite of that). So the Volar people literally had to monkey patch Node's the standard library and edit TSC's source on the fly to make anything, work, at all.
It all just goes downhill from there. Designing good APIs is hard enough - having to design them around the arbitrary barriers of some Microsoft boffins who decided to add a complex type system to a language which desperately needs a simple macro system - and took 10 years to do a shit tier job - oh, such an enlightening experience! Let's just say that I have become acutely aware that during the past year I have, well, degraded. As a person. And I literally attribute about 50% of that degradation to my ill-advised choice to do my work in TypeScript. Because like everyone I thought "hey, it's just a superset of JS, can't be that bad..."
I was reluctant to introduce a build step (having got used to the simple refresh cycle of a browser plus vanilla javascript) but I am pleased to say that this is a bit of a non-issue now with modern tooling like esbuild et al.
I still avoid NPM like the absolute plague, but the good news is that tooling like Deno have made typescript first server-side development in the ECMAScript world a total joy.
I've said it before - it's like floating through the air. So serene, so expressive. A true joy.