Hacker News new | past | comments | ask | show | jobs | submit login

It’s interesting to read comment threads of people that are dead set against Typescript. It’s a tool that has very few downsides and that improves nearly every single line of code you write. Either they’re scared to learn something new, not willing to take the time, or misunderstanding how useful it is. For anyone reading these comments and agreeing with Typescript naysayers, I would think more about why the commenter and yourself feel that way. You’re putting yourself at a big disadvantage.



As anything - "it depends"™. I did not notice "every single line of code" getting better at all. Yes, it makes things easier on a large team where people do not have time to do codebase discovery - or where people are moved to be highly interchangeable, on big codebases. Yes, static verification can help those teams and those codebases.

But it also introduces a lot of extra work "just to appease the type system". It rarely improves performance (if ever). Because TS has no runtime inference/validation, working with larger libraries or the browser can be a chore because half of your code are type signatures or casts.

So - not necessarily a naysayer, but I do believe that TS is oversold and with smaller teams/projects it might be slowing you down as opposed to helping.


I manage a relatively junior developer who has been using ts ignorer statements a couple of. times. I have said to him, that everytime he feel inclined to either use ts ignorer or do type coercion, he should call me first.

every single time it is a reasoning flaw implementing a solution that is sub par and bug riddled. Had they just let types guide them, they would have become better developers and not had broken the application.

I am curious though. can you provide a snippet where types would be a disprovement?


The worst I had to deal with was converting anything browser-native into data structures that would satisfy the type checker (dealing with native references), and the whole "struct or class instance" dichotomy. Specifically - when there is a lot of DOM-native input (like drag&drop events and their targets and the targets of their targets) that have to be "repackaged" into a TS tree (ending up with properties which would be a JS-version of void*).

An example of what I call "ceremony" would be

  interface BlockIndex {
    [key: string]: UploaderBlock;
  }
  const perServerId = {} as BlockIndex;
  uploaderFiles.map((fe) => fe.blocks.map((b) => b.serverId && (perServerId[b.serverId] = b)));
While somewhat useful, this is in internal code which never gets used directly, and there are 4 lines of ceremony for 1 line of actual code.


The ceremony is caused not by typescript but your misuse of map. You don’t need to create perserverid as an object first. Instead you could flatten fe.blocks, and then filter by b.serverId and then map to a key,value array and use Object.fromEntries to turn this into a keyed object.

Something like:

    const perServerId = Object.fromEntries(uploaderFiles.flatMap(fe => fe.blocks).filter(b => Boolean(b.serverId)).map(b => [b.serverId,b]))
And typescript infers the types correctly. But I still wouldn’t write it as one line, and I’d use lodash instead.


for most frameworks these typing are built. in (eg. react).

my expectation is that there are some packages/DOM typings so you don't need to write them?

regardless, your point stands: typing external dependencies is a pain.


Duck typing can lead to a false sense of security when you /think/ you have Foo when in reality you have Bar with the same shape.

Also Typescript sucks at keeping track of type changes in a single scope. While in Rust I can assign string to foo and then update it with int, I can't in Typescript. This leads to worse types or worse code for the same operation. Combined with typescript's lack of statements as values, conditionally initializing a value is pretty obtuse.

Those are the issues that come to mind right now.


> Duck typing can lead to a false sense of security when you /think/ you have Foo when in reality you have Bar with the same shape.

This is literally always your problem with javascript, its only sometimes your problem with typescript. It's a weird argument.

> Also Typescript sucks at keeping track of type changes in a single scope.

Isn't this considered a very bad practice? Also rust does not allow this, it only allows shadowing.

> Combined with typescript's lack of statements as values, conditionally initializing a value is pretty obtuse.

Can you give an example?


For the first one: It's not an issue in JavaScript because there isn't some compiler telling me yeah that's fine, I have to confirm myself.

For the second one: I know it is shadowing, what I mean is I find commonly that I'd like to have it in Typescript as well. In JavaScript is not necessary since I can just use the same variable.

For the third one: If I have some string variable that needs to be created from either one set of instructions or another, in Rust I do exactly that:

let foo = if x { ... } else { ... }

In ts your options are making it mutable undefined and mutate it inside the if else, using a very weird unreadable ternary, using an IIFE that returns into the constant, or creating extra functions to move the logic out. None of these are even close in readability, locality, or soundness to the rust example.

I find the _combination_ of those things that make it harder to write ts than js.


1: You are similarly able to confirm yourself when you ducktype in TS, regardless: once you've ducktyped once in TS you are then at least helped by the compiler. Again, this is really not a good argument at all.

2: This is a programming practice I never see and would seriously question if its necessary ever, let alone "commonly". I think you may have picked up bad practices from writing in dynamic languages. Please see this for a few example arguments against this practice: https://softwareengineering.stackexchange.com/questions/1873...

3: You are now debating that Rust has better typing than TS, which makes sense because Rust is made from the ground up to have extremely well done static type checking, whereas typescript has to comply with dynamic typing originating from JS. It follows trivially that Rust has the better design because it has more freedom to do what it wants. JS < TS < Rust


I am curious on any example where changing type in scope is more performant or more readable.

it is not really an argument against typescript that Javascript is so bad that you need to spent time tracking your changes.


While in Rust I can assign string to foo and then update it with int

When do you need to do that? Can you give an example?


I suspect they're talking about shadowing. You can't change the type of an existing variable, but you can create a new variable with the same name but a different type.


You can use branded types for the first case.


Even as a one person developer, you inevitably need to come back to old code and understand what's happening. Types help with that. The size of the team or codebase is irrelevant.


Small projects have a habit of getting bigger and small teams have a habit of growing also - usually to deal with the mess of the small project that is now bigger


Is that a bad thing? If you're building an MVP, do you really worry about how 2k developers are going to work on this 10 years from now?

Requirements change and the code base needs to adjust with those requirements, that's gonna happen no matter what. I've met a lot of people trying to predict future requirements, deciding to overenginer today for a brighter future. I have very rarely seen anyone guess the future requirements accurately.


Small projects become bigger projects much faster than that! I’m not suggesting that anybody should think too far ahead when it comes to building mvps, but if it’s a choice between a typed language and a dynamic one like JavaScript, baking a poor decision in early is going to hurt later. And later is much sooner than you think.

That’s not going to negatively affect your initial velocity, if it does, the team isn’t strong enough.

If the project is just a one off website or something genuinely small, sure, who cares? Otherwise it's worth realising that you'll be dealing with the fallout of poor early decisions pretty quickly.


> That’s not going to negatively affect your initial velocity, if it does, the team isn’t strong enough.

This. Note also that "a poor decision" might as well be "have developers fight the type system instead of delivering UI and pivoting if users don't like it".


There are also cases where small teams that have grown grow unproportionally to the size of the product, and while the product is set up in a fairly sane way (and there is little wrong with it!) having 20 fresh people swarm into it destroys both the architecture and the execution. And with a small team enforcing cohesion for both of these is much easier! So a small project might as well stay small, but this should be somewhat a priority.

Mythical man month in action.


Some people also hate parking between the lines and returning shopping carts at the grocery store. Those are similar in that they have negative value to the individual but help the community around them.

TS often can interrupt an individual's flow, so feels like a negative value. It's only when the whole team is using it on a bigger codebase with lots of changes that the benefits start to manifest.


Not just with teams, going back to a solo project after some time is so much more of a hassle if you don't have any types to guide you.


A million times this. Many a time I have done something "clever" to elegantly solve a problem in Javascript, only to come back to it a year later and not understand what the hell I did. The context for the problem wasn't fresh, so I didn't understand why I was doing that "cleverness", nor what restrictions there were on that solution, etc.

I rewrote one of those projects in Typescript a while back, and came across a similar "clever" solution (mainly having to do with dates having potentially multiple sources, so being in potentially multiple formats), and it made the code _infinitely_ easier to understand. So much so that when I came back to it recently, one quick glance at the types for that section of code gave me all the information I needed to confidently extend that code without worrying about bizarre runtime errors.

People forget that even in single-person teams, you're actually working with many different "people" over the lifetime of the project, given how different you and your understanding of the context of your code will be over time.


Imagine you come from a small town where there are no parking lines at all, and everyone efficiently parks on unmarked blacktop in a respectful way.

Now imagine you go to a big city where they have a bunch of lines in the parking lot and people only half use them correctly, parking over the lines, diagonal, etc.

The existence of lines doesn't guarantee good behavior. The absence of lines doesn't guarantee bad behavior.

This is the argument I see for javascript-only folks who don't necessary enjoy using "the worlds most bloated javascript linter"

For the record, I am a Typescript enjoyer and I use it in my personal projects as well as professionally, but even I can admit that it's not automatically superior to javascript and it has a number of really frustrating and time-consuming downsides.

It's very easy to type the args and returns of a function and protect callers, but it's much more challenging to work with types between libraries and APIs all together. Lots of `as unknown as Type` or even the dreaded `any` to try and cobble the stack together.


100%. Great to have type consistency. Terrible to deal with similar conflicting extended types in the enterprise codebase that make minor changes because someone couldn't figure out a compiler issue 5 years ago.

For the record I don't like the syntax either. Combining ES type spreading with TS type annotation makes for difficult reading in my opinion. Why settle for this bastardized language and not just compile something made to be strongly typed into js?


That's apples and oranges, though. Sure, if you have a dev team that "parks via the shuffle algorithm", sure, painting lines isn't going to help.

But if you have a dev team that is taking the time to efficiently park in a respectful way, if you paint lines, _you're going to make that parking job a hell of a lot easier to do!_ And THAT's the big win of Typescript.


> the dreaded `any`

You're dreading javascript


This kind of aggressive "there is something wrong with you if you don't have the same preferences and priorities as me" is such a turn-off.

I don't use it because the compiler is just too slow; waiting 2.5 seconds for even simple files is a massive pain. I want the old "CoffeeScript experience" where you compile stuff on-demand in development and output errors in the webpage or stderr. It works very well, is low complexity, and requires almost no effort. But you can't as it's just too slow.

esbuild doesn't typecheck so it's not an option. And hugely complex background builders are, well, hugely complex, it's not an option.

TypeScript-the-language may be nice, but TypeScript-the-tooling is not particularly great.

And even if this was solved: any build step will add complexity. The ability to "just" fetch /script.js and "just" edit it is quite nice. This is also why I've avoided CSS pre-processors since forever (needed a bit less now that variables are widely supported).

Of course different projects are different and for some projects these downsides are less pronounced. There is no one perfect solution. But there are definitely downsides to using TypeScript.


I don't think anti-TS people are 'scared to learn something new'. I'm sure most of those people write TS on a daily basis, because it's an industry standard right now.


I'm not against TypeScript, but I don't really see the massive advantage. I rarely see problems that are due to typing, and the downside is usually limited as I keep my JS on the frontend, not the backend. Regular JS/ES6 just flows better.


>I rarely see problems that are due to typing

This is a fallacy similar to the Blub paradox: if your language has a weak[1] type system, then it isn't capable of recognizing many problems as "type error". But stronger type systems can express stronger invariants. So something that isn't a type error in one language will be a type error in another. This changes how the programmer conceives of problems.

Example: missing a case in a switch statement isn't a "type error" in C or Java, but it is a "type error" in languages like Rust or ML, because they have sum types with exhaustiveness checking. Other examples: array bounds checks can be eliminated with dependent types; lifecycle bugs like use-after-free and double-free can be eliminated with substructural types.

[1] "weak" in an informal sense of "not very expressive"


The real Blub paradox to me is that the most powerful and expressive language is best characterized by minimalism at the language level.


Correct me if I'm wrong, but doesn't the Blub Paradox imply that languages dedicated to Code Golfing are at the pinnacle of expressiveness, and look down on everyone else's languages (for their extreme verbosity, compared to the golfing languages)?


No I don't think it's about compactness of expression, but rather what it's possible to express at all.


Yeah or even simple typos, or mixing up the order of arguments, are things that are hard to catch in regular JS (except at runtime) but trivial in TS.

I suspect a lot of people might have had bad experiences with codebases which overuse complex types or trying to type things like Redux which is messy. When I use TS for personal stuff I’ll typically be a bit loose about things like any in places where I don’t care (for now) and I feel it doesn’t add much overhead, but I have been using it for a long time so it’s become second nature.


This is a very fair comment, and you seem open to understanding why types are useful.

"problems that are due to typing" is a very difficult thing to unpack because types can mean _so_ many things.

Static types are absolutely useless (and, really, a net negative) if you're not using them well.

Types don't help if you don't spend the time modeling with the type system. You can use the type system to your advantage to prevent invalid states from being represented _at all_.

As an example, consider a music player that keeps track of the current song and the current position in the song.

If you model this naively you might do something like: https://gist.github.com/shepherdjerred/d0f57c99bfd69cf9eada4...

In the example above you _are_ using types. It might not be obvious that some of these issues can be solved with stronger types, that is, you might say that "You rarely see problems that are due to typing".

Here's an example where the type system can give you a lot more safety: https://gist.github.com/shepherdjerred/0976bc9d86f0a19a75757...

You'll notice that this kind of safety is pretty limited. If you're going to write a music app, you'll probably need API calls, local storage, URL routes, etc.

TypeScript's typechecking ends at the "boundaries" of the type system, e.g. it cannot automatically typecheck your fetch or localStorage calls return the correct types. If you're casting, you're bypassing the type systems and making it worthless. Runtime type checking libraries like Zod [0] can take care of this for you and are able to typecheck at the boundaries of your app so that the type system can work _extremely_ well.

[0]: https://zod.dev/ note: I mentioned Zod because I like it. There are _many_ similar libraries.


Do you ever see null or undefined access errors? As a TypeScript developer I haven’t seen one for many years.

Also, when you have types it changes how you code itself. When I change a schema or refactor some function, I don’t need to think at all to make sure I’ve updated all the code that depended on the old schema or API; just fire the TypeScript compiler and it tells me everything that needs to be updated.

I’ve also not seen any issues for a long while where I’ve missed some conditional case, because I use discriminated unions with switch statements more, something that looks weird in normal JS but is very useful with types, since it tells me if I missed a case automatically.

Add that I’m managing a team of engineers, and so I can easily make sure they’re also not missing cases either, by setting the convention and having them see the light.

Putting aside other things like for instance always knowing that we’ve validated inputs for API endpoints since unvalidated inputs are the unknown type and therefore effectively unusable; or always knowing we’ve parsed and serialized dates correctly since we use branded string types to distinguish them from any other string with 0 runtime impact; the list goes on.

So yeah, it might just be the case that you haven’t actually internalized what coding with types even means, so you’re unable to imagine how it can help you.


I always feel like those comments are written by people working on 2-person projects who never worked in a 50+ people shared codebase, and not understanding that the world different than theirs exists, and what challenges that brings.


Please don't sneer, including at the rest of the community.

https://news.ycombinator.com/newsguidelines.html


It's not that it's bad. But sometimes the project and the team are not that big that its qualities matter. And you lose a little bit of readibility with type-intensive code.

And nicely written TypeScript looks awesome, but badly written TypeScript can be a huge mess, as it can with any language, but TypeScript purists sometimes forget that the language is just a part of a nicely written and designed system.


I would describe typed code as more readable, not less. I take “readability” to mean ease of understanding, not how much my code sounds like written english. Not knowing what the type of something is makes understanding harder.


Inferred types seem to be an indication that even the most type-safe languages (eg rust) recognize that types hinder readability, at least in some way.


>You’re putting yourself at a big disadvantage.

Why not just appreciate the diversity of opinion and move on, rather than lecture people?


It's the Great Typing War all over again.

Some people feel more comfortable with JavaScript, Common Lisp, Lua, etc.

Some people feel more comfortable with TypeScript, Typed Racket, Luau, etc.

And that's okay.


> It’s a tool that has very few downsides and that improves nearly every single line of code you write.

Sometimes I just don't feel like dealing with those very few downsides though, but I can accept it's mostly personal preference.

At my age, sometimes I just don't want to deal with:

1. Yet another configuration file (tsconfig.json in this case). When something breaks, having one more place to look at is not something I want. The more extra files like this are needed for the development environment to even work (as in, something undesirable happens if you remove them), the less confidence I have in the project's long term reliability/stability.

2. That same configuration has misleading naming. The `"strict": true` setting should be called `"recommended": true`, or at least `"preset": "recommended"`, because it's not even strict. I would expect this `strict` flag to enable everything to the most restrictive way possible, and let devs disable checks (if) they don't want them. In its current state it doesn't enable strict checks like `noFallthroughCasesInSwitch`, `noImplicitOverride`, `noImplicitReturns`, `noUncheckedIndexedAccess`, `noUnusedLocals`, `noUnusedParameters` (I might be missing more).

3. Related to previous point: Inconsistencies between projects. So I work on one project with strict settings, tsc properly mentions possibly undefined accesses, etc; and then I move to a different project, and if I forget to context switch ("TypeScript config is different here"), I could be accidentally trusting the compiler to keep undefined accesses (and other stuff) in check, when it's not actually doing so.

4. Last time I checked, I couldn't just have a git repo "foolib" that is 100% TypeScript (100% .ts files, zero .js files), and `npm install` that repo on a separate project, and have it Just Work™. There's always extra steps that need to be done if you want to use .ts files from a separate package (usually compile to .js and install that; or using a bundler (read first point again)).

5. Why does the "!" operator even exist (or at least, why isn't there a flag to forbid it (for example the strict flag)). In my experience, using it is just developer laziness, where someone just doesn't want to write proper checks because "it's noise".

---

Those 5 points came off the top of my head so I'm almost certainly forgetting stuff.

It's mostly "death by a thousand cuts" kind of stuff, so sometimes I might not mind, but other times I might not be in the mood to deal with this and heavily influences my decision to go with TypeScript (keeping it approachable to as many people as possible) or a different language/ecosystem altogether.

Yes, I could "just" write a package that I can just npm install and it autoconfigures TypeScript and other stuff for me (and I have done so, for my own sanity). But I shouldn't need to do that, and it's too brittle for my taste.


Don’t let perfection be the enemy of the good


[flagged]


Please don't cross into personal attack, no matter how wrong someone is or you feel they are.

https://news.ycombinator.com/newsguidelines.html


People think typescript is really the silver bullet. If a developer writes crappy code, using tool x will not make him write great code. and the tool may not improve the project either. It's really tiring to work with people like you who are morbidly in love with a tool.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: