I am currently also working on a project written in a JS backend that's slowly porting to TS, for the same reasons, but I still would prefer to go back to C# and take it a level further. I just don't enjoy the TypeScript language all that much.
I am currently also working on a project written in a JS backend that's slowly porting to TS, for the same reasons, but I still would prefer to go back to C# and take it a level further. I just don't enjoy the TypeScript language all that much.
You say this sarcastically, but every JS-related thread on HN turns into a flamewar between type lovers and haters.
The type haters make exactly the argument you're mocking: that typeless languages do not result in more type errors. Their reasoning is along the lines of, "I don't need a type system because I'm a professional and don't make mistakes."
That sounds laughable or like an exaggeration, but it's a surprisingly common line of thinking. The buggiest code I ever worked on was a PHP code base written by someone who had been coding for 20 years and had a Master's in CS. Before I inherited the code, he told me, "I code in the terminal. I don't need IDE features because I don't really make mistakes in PHP anymore."
Again, seems satirical, but not uncommon.
You mean slower as in performance? I'm having a hard time understanding how types == performance, so you have to mean something else.
I guess assembly would be the language you would use if you really need to squeeze out the maximum amount of performance of a single CPU, and as far as I know, it does not have types.
Also languages built on top of LLVM could get by with trading compile times for run time performance. Maybe you meant compiling gets slower without types?
For a compiled language, the compiler could know that `foo` and `bar` are 32-bit integers, and thus compile `foo + bar` to call the addition function for 32-bit integers. In an interpreted language, the interpreter could do the same thing.
Without that typing information, both would have to invoke a generic addition function that detects the types of its arguments at runtime, notices they're both 32-bit integers, and delegates to the corresponding addition function.
Of course there are tricks that can be played in the weakly-typed case, like assuming the same call site will always have the same types, so that there can be a short path that assumes that and only branches rarely. That way the first or first few executions might be slow, but eventually they get almost as fast as the statically-typed case. JS engines in particular and JITted runtimes in general usually do this.
>I guess assembly would be the language you would use if you really need to squeeze out the maximum amount of performance of a single CPU, and as far as I know, it does not have types.
Assembly absolutely does have types. The addition function for 32-bit ints only operates on 32-bit ints, and is distinct from the addition function for 64-bit ints.
You can add any bits that will fit in the relevant registers. Assembly cares not whether the programmer thinks they are ints.
[0]: https://github.com/php/php-src/blob/3709e74b5e495e210ada8039...
Static types tend to push people towards copy-pasta code, as well, which can even work against performance.
Most of us are not optimizing for program speed. Machine time is cheap. Human time is expensive. The biggest cost-savers for most programmers will be the ergonomics of a language, not the optimizations available to the compiler.
Now I'm sure that many feel like that, but I don't think it's the majority, but I also don't know.
Here are some more arguments against types (don't necessarily agree with all of them myself, but for reference in the future when you want to write from the perspective of someone who feels more productive without types):
- There are other, more flexible ways to solve the same problems you solve with typing, the clojure.spec way is one of them
- You lose flexibility when suddenly everything is locked to each other by name. Even if Account and Person both has a first and last name fields, you can't use both of them if the function is expecting a Account and only use the first and last name. Then you need to add an interface, name it something, then make Account and Person be based on that. Now the function is locked to the interface, and so on
- Type checking is a very basic form of testing that doesn't solve the most common, annoying and hard-to-track down bugs, logic bugs.
- Metaprogramming becomes harder. Not impossible, just harder.
- Types (at least in TypeScript and other languages I've been using in the past) disappear in runtime, so you can't really use them. It's just a tool for you to tell the compiler what something is so it can say "no, you did wrong here"
Now I'm neither a type lover nor hater. I just want to use the right solution for the right problem, sometimes, that's making a program that will take longer time to write but specification is set in stone and requirements won't change, so types will help you a lot. Other times, it's in a very dynamic and experimental environment where you have to be able to apply changes as quickly as possible and minor errors don't matter as much, as long as you can move forward fast and test theories. Then you can go back and refactor things.
TS uses structural typing.
I'm just trying to help the person I'm replying to to see more arguments in favor of dynamic systems without types, not necessarily straightly aimed at TypeScript as they mainly mentioned just types.
This is only true of nominally typed languages (such as... most typed languages). TypeScript is a notable exception: values just have to have the same property names and types and they'll fit. The language's philosophy is basically to describe the way people write JS rather than prescribe how people should write it, so idiomatic JS is usually easy to express in TypeScript.
I see this argument frequently, and it doesn't make sense to me. Take the following function below in JavaScript and Typescript:
function fullNameUntyped(obj) {
return `${obj.firstName} ${obj.lastName}`;
}
type FirstAndLastName = { firstName: string, lastName: string };
function fullNameTyped(obj: FirstAndLastName): string {
return `${obj.firstName} ${obj.lastName}`;
}
In both cases, the function's argument is bound to the interface represented by FirstAndLastName. The difference is that TypeScript allows you to be explicit about it and can statically determine if your code conforms to this. Without static type checking, you're implicitly bound to that interface.Also, you seem to have a misunderstanding. TypeScript is Duck-typed. This means that interface compliance is based on structure, not name. So the following code is correct and type-safe:
class Person {
personId: number;
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.personId = 0;
this.firstName = firstName;
this.lastName = lastName;
}
}
class Account {
accountId: number;
firstName: string;
lastName: string;
constructor(firstName: string, lastName: string) {
this.accountId = 0;
this.firstName = firstName;
this.lastName = lastName;
}
}
interface FirstAndLastName {
firstName: string;
lastName: string;
}
function fullName(obj: FirstAndLastName): string {
return `${obj.firstName} ${obj.lastName}`;
}
let person = new Person("John", "Doe");
let account = new Account("Jane", "Doe");
console.log(fullName(person)); // Prints "John Doe";
console.log(fullName(account)); // Prints "Jane Doe";
Both the Person and Account classes satisfy the interface FirstAndLastName in structure, but they don't have to reference it at all.> Type checking is a very basic form of testing that doesn't solve the most common, annoying and hard-to-track down bugs, logic bugs.
First of all, challenge. Types can help with logic bugs just fine, but also my guess would be that type bugs come up more often. This includes typos, using the wrong variables, incorrect function argument order, etc. But I'd like to see any reference for that kind of claim anyway.
Second of all, no one says you can't still can't write tests even if you're using TypeScript.
Types are some work up front but like any tool once you learn to use them it isn't really a hinderance on prototyping or experimenting-- if anything they make it easier for me. They also make it easier to refactor, since static analysis tools can help with many refactoring tasks as well.
I like that they're optional and not enforced. However, I also like that my IDE visually indicates when I've violated that contract. Has boosted my effectiveness quite a bit. Ive defaulted to type hints almost everywhere, especially in any library code I write (I also try and write useful docstrings). For me, its especially important because I'm the most senior Python dev on the team, the other 5 are experienced devs, but just learning Python. Me taking the time to do the type hints and docstrings is immensely helpful to them, as they all come from statically typed languages.
That said, this is my opinion. That's what this "argument" comes down to. Arguing over opinions and tastes. Each has their own and their own reasoning. Doesn't make either side right or wrong. Id just say go in whatever direction works best for one's self or team. There's no one size fits all solution.
EDIT: my apologies to anyone who did not realize this was satirical
Funny, I usually see type haters claiming that they don't actually make the kinds of mistakes that are fixed by strong typing.
Which is a completely different sentiment.
Personally, I'm agnostic (I make mistakes in all phases and fix most of them eventually ;) )
As your typical type hater, my preferred argument is: yes, static typing prevents a certain class of bugs from happening; no, this class of bugs is not nearly as relevant as type lovers seem to be claiming. By an order of magnitude if not more.
Source: 6 years of being a core developer of a front end framework consisting of 1,500,000 lines of ES5 JavaScript and SASS.
It is that big because it implements a lot of things that other frameworks don't even try to try thinking about, things mostly used to build very boring line of business applications (think thousands of forms, huge grids, reports with charts, etc). Applications built with Ext JS can easily dwarf the framework itself; the biggest I've seen personally was ~30 mb of minified ES5 JavaScript.
...and it worked in IE8, BTW. :)
I used to do a lot of things on it, including _very_ deep refactorings with sweeping changes across the codebase. The secret sauce is focus on automated testing: when I left the company, we had ~70,000 test specs for the framework, and the test suite was executed on each commit to every PR pushed to Github. Depending on framework version (there were several in flight), and browser matrix (15+ supported browsers), each test run yielded 500,000-700,000 spec results (and finished in under 20 minutes).
There was a lot of custom tooling around this, of course. Including a test runner that I got open sourced right before leaving the company: https://github.com/sencha/jazzman. Sadly it looks like there were no new commits since I left; I hoped to pick it up later but so far I haven't encountered the need to run significantly sized test suites in massively parallel fashion. Nobody writes that many tests. :)
EDIT: I almost forgot that as a condition for open-sourcing the test runner they made me write a blog post about it: https://www.sencha.com/blog/ext-js-testing-story-under-the-h... :)
If your usual workflow is to defer all of the typing information to the IDE, rather than keeping it in your head. Then the second you can't access that information at a whim, you're fucked. It's like how I can't navigate around my city without Google Maps, because I've never had much of a reason to.
That metaphor works well, because Google Maps is probably a net positive. It's just that if I go around waving my phone in the face of a 60 year old cab driver that knows where everything is, going "I can't understand how you think you can get around without GPS, you're making loads of mistakes without realising it", I'm going to look like a fucking moron.
This is just not at all how it works in practice. You still keep it all in your head, but the compiler errors protect you from small mistakes.
No matter how great of a programmer you are, you will make transcription errors with the things you have in your head. This is literally exactly the type of "I don't make mistakes" nonsense that I said was common, and a bunch of other people said was extreme and a strawman.
There's more ways than type hints to fail fast. I'm not saying me and all the other JS devs don't make mistakes. I'm saying the mistakes we make are ones that we catch in 5 seconds because the modern web dev environment is set up to facilitate that. Typescript devs make additional mistakes on top of that beacuse they have tools that catch those mistakes quickly too. We don't, so we get conditioned to program in a way to prioritise not making those mistakes (possibly at the expense of other mistakes becoming more common).
The point is that someone with years of JS experience is going to be more productive with JS, and someone with years of TS experience is going to be more productive with TS. I'm not saying Typescript sucks, just that the cost side of it's cost/benefit equation isn't the same for every dev, whereas your shit argument assumes that it is because you only base it on your own experience.
It is interesting how "protecting from small mistakes" is touted as "solving our API woes", innit?
At some point it is liberating to admit that static vs dynamic is a matter of personal preference and nothing more. If I like it, I will find a thousand reasons to justify it, and vice versa.
I just wish everybody would be open about it: I like "type safety" and the warm fuzzy feeling it gives me, and nothing you unwashed heathens can say about tight coupling, increased incidental complexity, over-engineered APIs and productivity loss can sway my opinion! Why, my productivity is _increased_ with static typing, because I make up for hours of bikeshedding about which type better conveys the underlying intent with writing less null checks and unit tests! Take that, type haters!
Meh, wishful thinking.
This rings true, especially given that in my experience, most of the static typed people either never worked with dynamic languages at all, or converted from dynamic to static. Most of the dynamic minded people I know actually converted _from_ static typing after doing that for years (myself included).
The calculus is pretty simple IMO: if one has mental discipline to work with a dynamic weakly typed language, not having to mess with types and compilers is downright liberating: ye godz, just gimme that data! On the other hand the people who never experienced conditions leading to developing said mental discipline tend to abhor the idea of not having the "type safety" (cute marketing, that). Hence the chasm between Lisp crowd and Haskell mob, with JavaScript being the perpetual battleground somewhere in between.
I make the mistakes that are "fixed" by static typing quite regularly, but since I execute the code that I write, they don't survive.
Where static typing helps is the refactoring. You change some code; what other things have to change, where?
Although informing the process of refactoring, static typing, at the same time: (A) puts up some barriers against refactoring and (B) creates the need for a lot of the refactoring in the first place and (C) requires a more complete refactoring job than is minimally necessary to validate the idea behind the refactoring.
Refactored code doesn't escape the need for testing test. If you have some statically typed code that isn't tested well, and think you can refactor it just by making a few changes and then adjusting elsewhere to shut up the compiler, you have another thing coming.
It's almost as if different people have different working styles that make different aspects of a language useful.
This argument will never be resolved because it's dominated by psychological factors. Once you've adapted to environment A, A's costs (e.g. hoops the compiler makes you jump through / time spent tracking down type errors) become habitual and you don't notice them anymore. Meanwhile the costs of unfamiliar environment B are extremely highlighted in your attention. Similarly, if you like and identify with A, its benefits will be top-of-mind for you, while if you don't like B (and nobody likes B), you'll discount its benefits.
It gets worse. Sometimes benefits are costs until you make it through a learning curve. An example is parentheses in Lisp. They stick out like a sore thumb until one day they don't. Later you realize that they were a liberating force all along, and you now have anti-gravity powers you never dreamt of. (<-- This is an example of how people talk when they are adapted to an environment whose costs they no longer count and whose benefits are top-of-mind for them.) We can't even agree on what the costs and benefits are, let alone how to measure them.
It's no wonder that people feel so strongly about this dispute. When you don't count the costs of your preferred environment, and only count the benefits, it gets obvious pretty quickly that other people are idiots. For some reason the idiots feel the opposite and perversely insist on it.
It gets worse. Imagine that you're a decent, fairminded sort and so, magnanimously as is your wont, you decide to give the idiots a fair shake—you know, just to verify that you're being fair and whatnot. You fire up whatever it is (Clojure? OCaml? needless to say, you make a charitable choice) and start programming for a while. What happens? All the costs of unfamiliarity hit you in the face. Not one of the benefits that has lived at the top of your mind for years is anywhere to be seen. This thing doesn't even catch trivial type errors at compile time! / I can't even execute this bit of code in a REPL! This is a painful experience. It can even be an enraging one, for example if you are forced for some extraneous reason to work on a system you didn't create using tools you don't like. Most people who go through that experience once have their views solidified for life. Meanwhile someone else had the opposite experience.
Given how passionately people feel about this and the certainty in forum threads about it, it's curious (or maybe to be expected) that the question of published evidence comes up so rarely. HN user luu did the definitive survey on this: https://danluu.com/empirical-pl/. The short version is that such studies as there are don't address the core question and/or find insignificant effects and/or their designs tip obviously to one side or the other (and I do mean obviously, like comparing Peter Norvig's code to that of random undergrads).
Bias dominates every other factor, put together, times at least 10. As long as that's true, we can't say anything rational without genuinely accounting for bias. Do we actually know how to do that? The studies don't seem able to. These debates certainly don't, nor do they try. Maybe the interesting phenomenon here is actually the bias itself. Maybe it's not that we like an environment because we're more productive in it, but that we're more productive because we like it. Maybe we should get better at liking things.
The debate about this tends to remain stuck at a low level because once you've been around the block enough times you realize that it hurts to bang your head against a wall so you stop. Thus the vocal population undergoes a perpetual exodus of the experienced, but makes up for it with a fresh supply of enthusiasts, reminding one of the adage, "I arrive at the office late, but make up for it by leaving early."
TS solves problems I don’t have, and promotes a style I don’t want. I don’t want thousands of global symbols in hundreds of files in my project. At least TS has the decency to infer types in some cases, or at least put the type clutter after an identifier instead of before it, but it’s usually only superfluous clutter to me personally.
I realize that many people like TS. I just hope they understand why some of us are less enthusiastic.
Do you mean like curried functions?
I think you mean currying, right? It's been supported at close to the same level as Haskell for 3+ years[1].
> TS solves problems I don’t have
To me, it solves: 1) devs getting lazy on a team, 2) catching my mistakes before runtime, 3) correctly using library APIs, and 4) refactoring.
All of these might be low-value to you, though. I think #4 is the biggest time-saver because it enables automated refactoring (through the IDE) that isn't possible with plain JS.
> I don’t want thousands of global symbols in hundreds of files in my project.
I'm curious what you mean by this. Are you saying that TypeScript creates global symbols that weren't there before?
You can configure TypeScript so that it doesn't have any symbols from outside your project at all. For example, you can set it to import the types found in Node or on the web, or you can just skip them.
1. https://www.freecodecamp.org/news/typescript-curry-ramda-typ...
I also use an in-house library to explicitly apply arguments to return a a new function, which allows you to produce 0 arity or variadic functions.
I’m glad somebody has a work around for using Ramda in TS, but it looks every bit as “fun” as generics in Java, which I think have wasted more of my time than they have ever saved.
Like you, I think the truth is between the extremes. My original post was just pointing out that the extreme of type-hate is not uncommon here. The opposite appears to be true as well.
It's not really helpful to use a label like "type-haters" or "type-hate". I mean, it's helpful in the sense that it shows how tribalistic the arguments are (just replace "$X-haters" with enough Xs and you'll get the idea). But other ways of talking are more helpful.
On a related note: don't you think it's interesting how personal it feels? To some extent that's because the people who don't care that much select themselves out of the conversation to begin with. But I think it's fascinating even after pricing that in.
> don't you think it's interesting how personal it feels?
I take your point and will avoid it in the future. I used it precisely because, as you say, people are very emotional about it and take it personally. I meant to imply that these were not necessarily good-faith arguers having a rational discussion, but rather participants in a flamewar. This general thread seems to be a mix of both reason and flame.
As for why it's personal, I don't know exactly and do find it interesting. Part of it is likely that people have invested a lot of time into one stack or another, and that becomes an emotional issue. If I spent 10 years becoming a PHP expert, I'll be more upset if someone says PHP is garbage. It suggests my skills or prospects are poor.
After that, I think people are saying things like, "People who use TypeScript are stupid/lazy/etc.", or "People who don't use TypeScript are stupid/ignorant/etc.". Then the emotion may come from the conflict itself, not from the actual position someone is holding.
Nah. Where did you get that? That's so horribly wrong, it's not even funny.
Types are one tool in the toolbox to describe a system. It is not the most powerful or expressive one. It just happens to be available at compile-time. It catches trivial bugs, acts as documentation, helps the compiler with memory allocation.
It also is static. And that's where the weaknesses begin. A whole class of problems that benefit from domain definitions being available at runtime are clumsy to write in languages that force types on you. Not impossible, of course, but clumsy. (i.e. place-oriented programming)
To your point:
Everyone makes mistakes. The way to catch them and to prevent them in the future is not by having types. It's by writing tests. Tests > Types > no types at all
You conveniently neglected to include (Types + Tests) at the beginning of your little food-chain there.
That may most likely put "types + tests" at the bottom of their food-chain. And speaking of TS, since it compiles down to JS in production, it can be considered a very basic type of testing, or an advanced type of linting.
That is totally illogical.
If types are better than nothing, and tests are better than nothing, then how could both tests and types together be worse than nothing?
> Did you miss the part where they said "... at runtime are clumsy to write in languages that force types on you.."
No, I didn't miss it. I just disagree with it. Interpreting values at runtime is called "parsing". The parsing step exists regardless of whether the program is written in a static language or a dynamic language.
Ok, move it to the left by one, my point still stands: considering his goals and experience, types + tests will most likely be almost at the bottom of their food chain.
> No, I didn't miss it. I just disagree with it. Interpreting values at runtime is called "parsing".
Actually, you completely misunderstood it. It has nothing to do with parsing. Its all about design patterns, functional + dynamic language design patterns to be more specific.
2. I do not understand why you are so hostile. To try and understand, I went through some of your comment history, and I can see it's not just me you are hostile towards. Furthermore, you appear to have a poor understanding of how JavaScript works. For the former reason, at least until your anger issues are addressed, I don't find it constructive to continue discussion with you.
[0]: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-typ...
> your little food-chain there
> That is totally illogical
> you appear to have a poor understanding
I have not yet looked at your comment history, but just looking at your comments a few lines up, that's what I've found. And the first two are your comment to someone else. Ever heard of projection? That is what you are suffering from if you cannot see that you are just as hostile as I am.
Personally, I respond in kind, so I am very much aware of when I'm being hostile. And unlike you, I never resort to cheap personal insults about the understanding or abilities of the people I engage with. That's just beneath me, my friend.
Went to a tech meetup group hosted at a moderate sized local tech company. The tech company had job openings, and I'd looked at a couple earlier that month. Some of their staff came to the meetup, and I got to talking with one of the 'lead' guys. I'd recently started using IntelliJ, and was talking to some others about how much more productive I was being, and it was the best money I'd spent on anything in year.
The lead guy jumped in and basically said IDEs were for wimps (more or less). If you're a good developer, you don't need an IDE - he knew everything about their codebase, was very productive, and had been for years.
My experience was the opposite - I do a lot of freelance/consulting/dev stuff - my life is jumping between projects every few months or year or so. Having tooling that helps me understand so much more about a project than I could get just from vim (for example) was so eye-opening and transformative to my way of thinking that it made me re-evaluate a lot of previous notions, habits and assumptions about development. I'd explained that using good IDEs had made me better at my craft in every conceivable way.
He just sort of shrugged and said he could understand why some people might need an IDE, but they're mostly just crutches for people who don't want to spend the time to learn a codebase the 'real' way.
I'd been interested in applying to that company, but didn't bother after realizing I would be having to work with that attitude daily.
See either Emacs or any editor using LSP such as Vim/NeoVim.
Otherwise it's a joy to work in, imo.
Trying to go from TS back to JS is a frustrating experience to say the least.
Doesn’t work so well with an existing codebase, but still.
The resulting JS is FAT, don't get me wrong, but since this is for internal use and it's heavily cached, I don't mind having our users download a 3MB JS file.