If you are starting a new project, yes use typescript over JS. And configure it with all strict options.
This doesn't magically give you well architected and well-tested code, it still takes a good engineer to do that. But what it does allow is for the engineer to refactor the code to support new functionality with more speed and confidence, rather than shoehorning it in in fear of breaking something or it taking too long. Furthermore, as people join your team, it will be a lot easier for them to figure out where some piece of data is coming from and how the code flows from one spot to the next.
If you have a massive JS project and you want to convert it to typescript. The answer might be no. Oftentimes when people port a JS project to TS, they end up using non strict typescript, and you get the syntactic overhead of writing TS, without really any benefit because nearly everything is any typed. If your only concern is a better codebase, and it doesn't matter how long it takes, then sure it is worth it, but we all know that is never our only concern. It still might be worth it, but I can almost guarantee you from experience, you will still end up with TypeErrors in your console at some point, because people will have just any typed things.
Also if you're writing a tiny node script in a single file, to do some simple operation, then no it is probably not worth setting up a command to compile it and a tsconfig to configure it. Unless it is doing multiple things, and/or someone else in the future will have to use it, then it is probably still worth it.
Typescripts inferred types, can do some crazy things, and can be very powerful. This to me is really fun, but has definitely caused me when I first started using it to get lost in having fun doing crazy things, to ensure my strings were exactly some set of values depending on what some other value was, when a simple unit test and a simple "string" type, would've been a lot quicker and less confusing.
If the strings are static you can define a type that's a union of string literals, create a tuple containing them all, and then a type guard function that simply checks the argument against the array. If the strings are dynamic then you're better off looking at a library like newtype-ts, but it's not very ergonomic compared to the likes of Haskell/PureScript so use sparingly.
Just my personal preference but using these string union types means that you don’t have to import the enum. Plus it’s less verbose and translates back to JS better than an enum does.
Yeah the stuff I was doing was more to see how crazy of things we could do with the type system. Like I said just fun. But probably a waste of time other than the learning. Just a warning that is tempting to do crazy (but stupid) things.
One kind of weird thing we did, was we had these JSON Schema definitions that were const, so all the strings like "type" were of a literal string type. Then using recursive type definitions, and the `extends` key word we were able to extract the actual shape of the data into a type.
Not exactly that useful, compared to typing it out, but it was still kind of cool.
This ignores the many actual situations where people and teams and companies have migrated from JS to TS and seen immediate benefits and improvements, and discovered bugs simply as a by product of the migration, even if they’re not using very strict typing.
I have a use case where I'm using typescript to get type hinting in the model layer (mostly just interfaces describing API schema) and reducers - it helps document store structure, catch typos and quickly lookup available properties/see model structure.
The rest of the project is ESNext/babel - we have to interop with an existing codebase, typing everything is very low value compared to overhead - similar to writing unit tests for UI components.
TypeScript feels worth it until you use something like Elm, then you realize just how lacking and TypeScript's type system truly is.
I have grown rather weary of type systems that don't require you to be exhaustive and, for my money, types like `Omit` and `Pick` are nasty hacks that just let you pretend like you aren't doing dynamic types, something TypeScript does a lot. The TypeScript pattern of `someFunc<typeof someInput>` is just dynamic types with extra steps. Using `any` wouldn't really lose you much of anything, the "type" is just "whatever this input is". That's not a real type, that's a dolled-up dynamic type. But hey, it typechecks!
TypeScript's major weakness is that it doesn't want to break away from Javascript. That strict dedication to being a superset means the type system explodes into ten thousand builtin types the come, seemingly at times, from nowhere, simply to control for the wildness of Javascript. It feels like a joke sometimes, and like I need to have encyclopedic knowledge of all these highly-specialized types that are included in the language and are often being used in the background.
This leads to nearly impenetrable error messages regarding types, especially if you have a stack trace involving more than one object/record type (whatever you want to call it), it will expand all of them and just, no. It's unreadable. It can be read, but it cannot simply be grokked.
TypeScript just doesn't impress me. I get far better results from other languages for a whole lot less work. TypeScript claims to want a balance between correctness and productivity but the language gives me neither of those things. My typical experience is that I wrote almost the same amount of actual code, but now also with types. Elm lets me write far less total code. Less code means less bugs and more productivity, and better types and better correctness is what gives you those things.
I think this misses the point somewhat, I think even the typescript people will agree, yes, you CAN pick another language and get better type support ( amongst many other things ). But typescripts goal is to be a superset of javascript, which has resulted in many many libraries now shipping with typescript type definitions as officially supported things within libraries. Elm ( or any other client side language) simply hasn't had that kind of impact. Yes it does still have a bunch of problems inherited from the nature of javascript. Sure, you, as an individual might get good results with your tech choice.
So I think the main thing is typescript should make things better in the javascript world, but due to its ability to gradually adopt typescript syntax over javascript syntax you can live in a middle ground where it's not going to do much for you (or even make it worse).
Ultimately I think compiled WASM based languages are going to take over, but we aren't there yet.
A big problem is that elm has questionable stability as a project. It's mainly one person, and not a lot of mainstream usage. The biggest projects mentioning elm are ones related to the main programmer.
Typescript on the other hand has had massive growth and has been adopted by a large amount of libraries. Now, web ts/js libraries usually suck but it still speaks to the overall health of the language in a community. Even then, typescript still only has a very small percentage of usage.
Elm is basically a rounding error on some of the surveys I've seen.
Long term support, tooling, etc are often much more important for languages past a threshold. Typescript as a language may not be super exciting. Typescript as a technology is wonderful.
Sadly, Elm was never able to build a true core development team outside of Evan. Or at lest it didn’t do it fast enough.
Evan is a smart programmer but not a strategic organizer or a communicator.
As a result, one of the most exciting and promising approaches to front end development was relegated to a nice-toy project status.
If you want to take the risk of building something using Elm’s approach, you’ll do it with Reason.
Maybe not 100% as nice as Elm, but you’ll feel confident you’re not risking your code base and company on the whims of one person.
I still think Elm is a nice educational language for getting people (and kids) into FP.
> TypeScript's major weakness is that it doesn't want to break away from Javascript.
No, it is not. That is exactly the major strength of Javascript! It is an addition to the landscape, for people, who can make use of it.
For everyone else, there is already Javascript "successors", that break with Javascript, but compile to Javascript. But they also are a new language, or, another language. You named one: Elm.
Not so TypeScript. I can gradually upgrade my codebase. And I can reuse knowledge and components, I already have. I am so happy about Typescript. How else could I continue using ES3 on the Windows Scripting Host and have modern Javascript tooling? For me TypeScript is the "better" Javascript. An evolution, rather than an exit strategy.
I'm not really sure why you were downvoted since you raised some pretty relevant points.
> TypeScript's major weakness is that it doesn't want to break away from Javascript.
This seems to also be the source of it's popularity, it's the path of least resistance, sprinkling types on top of your existing codebase is a way easier sell.
Maybe because it's totally missing the point? It's like if I ask "is mypy worth it?" and someone answers me: "not worth it, Rust has a better type system".
Also from a business/management POV, it's quicker to migrate JS->TS than JS->Elm and easier to find TS devs (because nobody uses Elm btw) or teach TS to JS devs.
TypeScripts pragmatism is indeed its biggest strength. It is very simple to get started with converting a JS codebase into TS. This means there is a _real_ and _practical_ migration path for all the mountains of existing JS. I haven't used Elm but I would assume the cost to convert existing code is much more significant.
But TS then goes further and layers a lot of really nice features on top of the JS underpinnings. Being able to strongly type string-ly typed things are, again, such a useful feature to have for a codebase that interacts with JSON/JS.
All this said, in my experience `Record` is not very heavily used, but is really useful when you do need to reach for it.
I haven't played with Elm, but my experience with ReasonML matches yours. I made some small but non-trivial side projects to learn ReasonML and TypeScript, and not only is ReasonML's type system vastly more satisfying, I actually found it much easier to learn (coming from JavaScript) than TypeScript is!
Typescript is entirely about being a superset of Javascript with the unique ability to implement types from very strongly defined to completely dynamic, and this can be set function by function or even line by line.
That's what led to the wild productivity and popularity. If you're answer is to just use an entirely different language then why stop at Elm? There are dozens of strongly typed languages available.
Nitpick: In the situations you described, the type is known to the compiler at compile time no? Do you have the same problem with "auto" in other languages?
Can I, in Elm, bring over my couple dozen NPM modules I use in my projects? At least what TypeScript has going for it, if not a more robust type system, is interoperability with all the other tools I need to build web applications.
Maybe not in the way JMTQ is expecting. You could bring your npm modules over and call them through Elm ports, but you’ll still have to declare type signatures of those ports and have a bit of ceremony about calling them.
It might be better to ask what those npm packages are doing, exactly. There’s a decent chance that either the npm package is filling a shortcoming of the JS standard library (e.g. moment) where that shortcoming doesn’t exist in Elm, or an equivalent Elm package exists.
if ('error' in result) {
console.error(result.error); // ok: result inferred as 'Failure'
} else {
doThings(result.value) // ok: result inferred as 'Success<T>'
}
Even though you used string there, it's pretty type-safe because if you have a typo in your string, the inferred types will propagate other type errors
if ('errors' in result) { // typo
console.error(result.errors); // Type error: result inferred as 'never'
} else {
doThings(result.value) // Type error: result inferred as Success<T> | Failure
}
Why is this better than throwing errors or using Promise.resolve()?
I know it is inefficient because creating a new error with a stack trace each time is bad for performance. However, you can also throw errors which do not extend Error to avoid this. Destructuring the output and explicitly checking for errors every time seems to be quite the hassle.
Because the error/success is encoded in the type, you are forced to handle the error case. If you aren't also wrapping throwable functions in `try/catch` then you have a lot of unhandled error cases.
If you know that something truly isn't going to error then you can just force cast it as `foo as Success<T>`. That will still blow up at runtime if its not a `Success`.
Alternatively, you could introduce a monadic chaining that is able to pipe `Result<T>` objects through many functions then handle at the end.
Check out the chapter on handling errors in the book “Programming TypeScript”, it gives a solid pattern for achieving something very close to Rust’s Option.
Mentioned as an afterthought at the end: IDE support for typescript, the best part about using the language.
Not mentioned: type annotations serve as a form of documentation that gets statically verified. The verification strategy has some cracks, and there's a (fairly small) chance that the documentation will end up misleading. But API documentation for javascript projects generally sucks, and typescript helps to fill a lot of the common gaps.
i'm tired of devs that claim that TS and GraphQL are "self-documenting" and don't need to write any documentation. it doesn't fill in the common gaps, it's just an excuse to not have to think about or talk to users.
In my experience it helps devs move the documentation up one level. By that I mean they don't just document the mechanics and type signature of the function, but instead document what the function is used for and why it exists.
You can already write decent docs in plain JS, but I've found that having the types already documented by the compiler helps devs move up that level of thinking automatically.
GraphQL libraries often have built in documentation abilities that make it trivial to document, extract, and make pretty api docs fairly trivially. It’s still like pulling teeth to get developers to use the functionality at all.
That said, the appropriate reaction is to laugh louder the longer they make the claim.
While TypeScript isn't perfect, it surely is a major step forward compared to plain JS! Newer features like conditional types and mapped types open up new possibilities. My colleague actually recently gave a fantastic talk about these advanced features at the TS Berlin Meetup, highly recommended watch if you want to see what it's like pushing the boundaries of TypeScript: https://www.youtube.com/watch?v=PJjeHzvi_VQ
Checks don't need to be in the JS, they just need to be there at compile time. No differently than a binary generated by Haskell; and you have source maps to replace debugging symbols too.
And as mentioned by another user, you can choose your compile target to a specific version of JS which does support object spreading.
Actually, Haskell compilation produces more type information available at runtime than Typescript's compiler does. For example, algebraic data types turn into tagged unions at runtime on Haskell, allowing pattern matching to operate on them.
Whereas on Typescript the compilation process erases all this information, making it impossible to evaluate many such expressions.
> algebraic data types turn into tagged unions at runtime on Haskell
How does that work? I know next to nothing about the GHC, but in Haskell I would assume some tag is associated with each constructor (probably an integer) for the ADT and that the actual pattern matching gets compiled down to simple comparisons to either that integer or the right function for the more complex cases (pattern matching strings and other values).
TypeScript essentially does that, except that the tags are not going to be optimized by the compiler (they will remain strings if the dev uses them), and the "pattern matching" is just regular conditional checks (if, ternary or switch) with even the exhaustiveness check being done as a manual hack introduced by the dev and checked at compile-time (the else or default case assigning the value to the never type).
As for runtime values, Haskell also needs to validate types when deserializing, although that will be done by the deserialization libraries such as Aeson instead of deserializing and then optionally validating like in TS.
> except that the tags are not going to be optimized by the compiler
These strings should all be === at least if they are compiled together (the JITter could even intern them to make sure), I believe -- so it will typically be a single 64-bit ptr compare whether it's a string or int. Only in the case where you're constructing the DU tag at runtime for some reason would you need to compare the strings.
Yes, you're right, it should be an integer comparison check ultimately.
> TypeScript essentially does that, except that the tags are not going to be optimized by the compiler
What do you mean by this? Typescript erases type information during compilation. So you would not be able to emulate pattern matching against types on TS. Or you could if you manually added some common tag to every single data type like interface.
Or are you talking about just compile-time type checking on TS?
> Or you could if you manually added some common tag to every single data type like interface.
This is how you emulate them, I took it for granted and neglected to explicitly mention this is a moderately popular convention in TypeScript (those are the tags I referred to). fp-ts for example uses it all over the place.
With this the end result is essentially the same with compile-time type-safety for everything, and compiled down to an untyped binary or an untyped JS blob.
> Still compiles Down to js where you lose all type checking.
And C, Rust, Haskell, etc., all compile down to native code where you lose all type checking, so what? Outside of compiler bugs, what is statically proven at compile time doesn't change at runtime. (Yes, C also has a notoriously loose and abusable compile time type system, but that's a different issue.)
> Things like spreading objects I to each other
> {...a,...b}
> End up being a nest object assign which is actually slower than spreading.
Yes, if you use the default target (ES3), typescript emits JS compatible with downright ancient browsers which is super portable at the cost of being less efficient than targeting modern JS features, like spread syntax which was introduced in ES6.
You can also set the target to newer ECMAScript levels, including “ESNext”, at the cost of compatibility.
> C is an infamously unsafe language, so the question might rather be what _your_ point is.
Feel free to insert Rust or Haskell or whatever your language of choice is in place of C and my point remains the same. Sure you can include type information inside the assembly output and some sort of runtime code to handle that, but that doesn't change the fact that your compile target has no type system. Typescript could do the same if they desired, and in fact I think they offer the option.
Compiling a language with a type system to a language without is not a knock against that language's type system. Compiling typescript's decently comprehensive/expressive compile-time type system to javascript's basic runtime type system actually seems like a step up compared to a lot of other compilation targets.
Like I have said elsewhere, Haskell and Rust and other such languages actually force you (to varying degrees) to write programs that are soundly typed at compile time. TypeScript explicitly doesn't, and instead treats program types in a way that's far more like Java or C# (which rely on both compile time and runtime type checking to enforce a program's types, and even at that are not entirely sound) without the runtime type checking to back it up.
> Typescript could do the same if they desired, and in fact I think they offer the option.
They very explicitly do not and have stated severally that it is not within the goals of the project to do so, which is something anyone actually familiar with the language should know.
> Compiling a language with a type system to a language without is not a knock against that language's type system
When you don't cherry-pick statements out of context, the point of what I said becomes rather obvious; if a language doesn't actually guarantee type safety at compile time, having a primary compilation target that takes a fascinatingly lax approach to data types is objectively worse (wrt type safety) than having one that's more strict.
Your posts don't make any sense. Rust compiles down to assembly too, but it's a safe language, so what? The compilation target has nothing to do with the relative safety of the language.
> The compilation target has nothing to do with the relative safety of the language
Yes, obviously. Almost as though the fact that all languages end up as x86 or ARM opcodes at the end of the day has literally nothing to do with the merits and demerits of their respective type systems, but people keep bringing it up like some kind of gotcha when type systems get criticized.
Mostly due to the way it's used for performance reasons. You can take other typed languages and pass around object information in byte buffers instead of structs as well, and subsequently lose all your type checking.
Why would you want runtime checks over static checks? The one thing I can think of is type asserting input from other js code, but surely you just want that on the entry function to your code, not in the internal bits.
TS compiled can still be faulty if the app is injected with data of different type
Runtime type checks is the lost link between static checks at compile time and arbitrary i/o data type in compiled js code.
You define a type in a similar way you would define TS types. At compile time it would give you the typescript type checks.
At runtime it will help you determine if data has the correct type exactly as your typescript app expected.
There is extra work though, handling error if there is a faulty data type
This is an often expressed sentiment that I feel misses the point by a mile, which is that Haskell (as well as other languages in its class) _forces_ you to actually write programs that are soundly typed at compile time. TypeScript very explicitly does not, instead taking an approach to typing that's more in line with languages like Java or C# (which rely on both compile time and runtime type checking to enforce a program's types) but failing to provide the runtime type checking to back it up. And even that wouldn't be so much of a problem if it didn't compile to a language as dynamically and weakly typed as JavaScript.
I disagree. The point to which I refer was stating that because language A with type safety of some degree compiled to another form B without it obviates A's type safety altogether.
The destination isn't the point; it's the compilation of the beginning.
The type of problem that TypeScript solves is not the type of problem that I have.
In my 15 years of engineering, exceedingly rare is the case where the underlying cause is due to using wrong types. I could count the number of times types has been problem on one hand. Instead, almost all issues are due to logic errors and missing or poorly understood business requirements.
For me, the time spent resolving TypeScript specific issues (missing type definitions, lack of support in third party libraries, general finickiness etc.) does not make it a worthwhile investment. I also did Scala for several years professionally, and while it instils rigor, I feel much the same that the price you pay isn't remotely worth the benefit you get. Conversely, I think one of the best investments you can possible make into tooling is to reduce the edit-compile-view cycle time, and you should aim for seconds at the most if not instantaneous updates. That speed of iteration will pay dividends.
> In my 15 years of engineering, exceedingly rare is the case where the underlying cause is due to using wrong types
The thing I've come to realize when using languages with more robust type systems is this:
All errors in code are type errors, but not all type systems are expressive enough to fully express the invariants that logically apply to a piece of code.
That said, in nearly 40 years of programming, I've frequently encountered type errors in both my and other people's code that a type system like TypeScript’s is sufficient to statically avoid.
I did that a lot at my last job and found that the debugging was pretty quick. There’s always some race condition from some asynchronous function somebody called without waiting for the callback. Does TS help with that?
if something can be returned as undefined instead of a function, then yes, you are forced to either write code to handle the undefined value, or modify your logic so the returned value can only be a function :)
I thought the same but the more I use TypeScript the less I care about it's typechecker. I use it mostly for the amazingly powerful IDE integration and so I don't have to remember if a `User` has an `id` or a `userId`.
Isn't this solved if you treat user defined (re: the developer) Types as an expression of business logic? Not so much as a way to signal inputs & outputs, but a way to expression why, how, and when those inputs & outputs are augmented?
Just having the ability to tag a functions param type as `string` is otherwise useless, other than trivial callsite validation, for instance.
> In my 15 years of engineering, exceedingly rare is the case where the underlying cause is due to using wrong types.
That just means you've never used a powerful enough type system. With a powerful type system, it's incredibly easy to make type errors. You'll find that you spend a ton of time fixing all of these type errors that suddenly appeared in your code.
Instead of focusing on what TS does not, focus on what it does.
TypeScript is not sound; JavaScript isn’t either. The first one will catch some type errors, the second will catch none.
Syntax highlighting, linting, testing, and now type checking: every step can make you more confident about the code you ship, before it even hits the browser.
You can forgo using any help and probably you’ll code faster, but, again, you lose confidence.
FlowType has better soundness than TypeScript. I like to think of TypeScript as a "feelgood" typesystem (especially how it handles predicate functions, type variance in function arguments, and type casts). The only downside is that Flow's typesystem is volatile. You _might_ find it painful to upgrade between versions.
With that being said, I still prefer Flow. Not sure how relevant this is, but at my company we mostly write Flow in the teams. For libraries I tent to provide both (code being written in flow, with with .d.ts provided). I also maintain both 3rdparty ts and flow type defs.
TS tooling blows Flow out of the water, though. But, I've heard flow team at facebook is focusing a bit more on DX and on making the typesystem a bit more ergonomic and expressive. :)
This is the dealbreaker. Building Flow in OCaml was a mistake, full stop.
I'm not convinced the level of soundness we're talking about is important except for the people who like to wax philosophical about PL theory. TypeScript has never been "unsound" in a way I actually care about and I would argue if you get caught up in a soundness issue you're already doing something completely crazy.
It's true that ocaml has different contribution entry profile. The only "issue" here is that flow being a js tool, naturally would attract more js contributions, but that's it. You can't say ocaml itself is not fit for purpose because this kind of tool is pefect use case for ocaml. It would be interesting to see flow rewrite in flow itself thought.
It being written in OCaml is why the tooling is poor. TypeScript integrates perfectly with most build tooling (e.g. Webpack) because it's just JavaScript like every other tool. To run Flow everything has to be shelled out to an external bin which adds a lot of flakiness and manual configuration if you want to integrate it with your build config.
I understand OCaml is great for writing meta languages (ha) but it's bad for the end user.
I use flow as well at the company I'm working for. I found lightweight setup that works surprisingly well for me. It started as experiment to use types in comments. There is no transpilation phase at all, just pure js with types in comments. All my projects use simple npm test "eslint . && flow check && jest" and there are no prepublish hooks, no manglings, no source maps etc. It means you can edit linked modules in place and get instant feedback. The workflow is very fast and non intrusive. It looks like this [0] or this [1] etc.
And compilers are effectively a rudimentary form of test. They are not sufficient, nor necessary but they do validate some basic assumptions about the code.
They also remove redundant testing (you only need to prove something fulfills certain properties once as long as you have a type for it); and the more advanced the type system the fewer tests needed.
It's not just about confidence. It's also about the speed you can use/refactor the code. Suggestions, type information on hover and instant feedback if you forgot, used wrong order or type, misspelled or simply don't remember exactly signatures or auto generated documentation means a lot when writing code. You can move much faster when working on typed code. Nobody ever claimed that types are replacement for tests. It just means that your tests don't require obvious clutter anymore, you can focus on more complex/important test cases to cover.
Tests only help if they are written, and if they cover the code in question. Both of things are unfortunately not always given. If things are not covered by tests type systems are still better than nothing.
Javascript is dynamically typed -- that does not mean it has no types. You are free to add dynamic type checks anywhere you want. I view these as critical for APIs even if you decide to use typescript. I also view them as critical for third-party interfaces where you may not be able to test integrations completely.
Doesn't typescript enable things like renaming an identifier and all it's usages, without doing it as a global text replace, or worrying that you might have renamed something you shoudldn't? Or is the type checking so "leaky" in a typical project that that kind of luxury (That would be done every day by Java or C# developers etc) isn't really available anyway?
Unless it's a massive overhead to use typescript, autocomplete, renaming etc seems like they would make it worth it on their own.
This will depend on the project, as TS can go from very loose to very strict depending on the settings. For a project with all strict settings turned on and casts minimized, then I find refactoring is an absolute breeze. I have successfully done huge refactors that completely worked without any (known) regressions as soon as all the tsc errors are resolved. And your editor combined with the language server drives a lot of the refactoring (similar to Java/C# as you alluded to) To me this is the real killer feature of TS over JS.
There has been some research done on how programmers interpret gradual typing systems, and it turns out that it breaks many expectations and ends up being confusing.
Note that the paper is concerned with run-time type checking and glosses over the fact that most/all of those code samples in the survey would be rejected at compile time by a static type checker (or at least by TypeScript).
Their conclusion that "erasure" is disliked and unexpected is effectively saying the underlying dynamic language's behavior is disliked and unexpected.
Of course it's possible to deliberately or accidentally coerce TypeScript into violating its type constraints and behaving in JavaScript's disliked and unexpected ways, but the survey questions would have to be substantially different, and likely the responses as well.
The soundness of the type system is not a valid criticism given that it's aiming at improving a language that had almost no type system at all. If you need more from your types then perhaps TS or JS is not the right choice here.
My take away from this: If you don't have the discipline on your team to enforce good structural standards for your code, TypeScript may not help as much as you think.
In my very much opinionated opinion, all the issues highlighted in this article, are avoidable with:
1. Don't use the `any` type (I'm actually pretty shocked this article doesn't talk about using `unknown` instead of `any`. I have found `unknown` can be used where `any` tends to be used, most of the time, if not all the time, and it comes with the benefits of things like assertion control & tsc's type interference (it acts almost as a marker, in some ways, basically, for the type that gets fed through. It also forces you to think about validating the input)
2. Enforcement of strict options for the compiler
A non-goal to me is using TypeScript to avoid writing tests. This should never be someone's idea in adopting any typed language. I think this is a smell of a bigger problem. Types or no types, it does not influence why or how many tests I write, as testing is about validating your programs ability to handle its inputs & the correctness of its outputs (your testing algorithms, at the end of the day), so I do not see how this factors testing at all. Types also do not negate the fact you need to do validation (beyond perhaps, trivial validation of arguments at the call site, if your codebase is actually following good structural patterns)
Is TypeScript perfect? No.
Should you use it? I think you should probably make that decision after a careful reading of the documentation & discussing it with your team.
Do I personally think this article has a strong case against TypeScript? No, as its (to me) very trivial to avoid any of these problems.
You know what would likely be more effective with most code bases I've seen though? Good documentation & comments, with explanations of what things do if it isn't obvious enough to pick up in a second or less glance. Preferably following the JSDoc standard.
1. Culture and existing code. Popular libraries use any when they should be using unknown or just not de-serializing stuff and making you explicity parse it yourself. The reason why these are popular is because culture and because of #2.
2. Creating type guards is a pain, especially if you're trying to go from unknown to something with a complex structure. If types were something you could reflect on in run time, like most other interpreted languages, this would be a lot easier.
To address culture, someone has to start doing it better, no reason it can't be those who know there is a better way, so I treat that as a red herring myself. You could always make a strongly typed wrapper around a library, for instance.
Creating type guards might be a pain, sure. I think assertion functions will go a long way in this regard
I also think that its an overestimated pain. If you are using interfaces to type your objects (commonly, this seems to be the pain point) then I don't think its terribly difficult to construct type guards around that.
There is also the reflect-metadata polyfill (that I think TC39 is eventually going to make a standard) for run time type information. Using decorators could be valuable combined with `emitDecoratorMetadata` option
the title completely doesnt match the content, this is pure clickbait and logrocket is so shameless about it. i grit my teeth and click but am completely unsurprised at yet another low quality list of arguments that have been rehashed over and over.
they mention a time tax of 262 seconds median for TS annotations for their set of 400 bugs. almost 4.5 minutes. so idk, there seems to be some math missing in this paper about the actual trade offs. so maybe you get a 15% improvement in "detectable" bugs found, at a 13.3% time cost (60 minutes /4.5 minutes) per hour of work. worth it...? are your devs expensive? more expensive than QA.
Thing is you can now have many of the benefits of TS without using it in your own code. Most popular libraries come with type definitions now and an editor like VS Code will take advantage of this and give you the same autocompletions TS would.
TS introduces some friction for setting things up, it slows you down in initial development, increases LoC, etc. I do like the type documentation in function signatures, the autocompletion and the refactor support, but I think the project needs to be of a certain size for the cost/benefit trade off favors TS, typically a project that goes on for 6+ months and with 2 team members or more. Anything smaller than that and I wouldn't choose it myself.
> ...there is an argument that states that the adoption of TypeScript would not have been as high if a sound type system is 100% guaranteed. This is disproved as the dart language is finally gaining popularity as Flutter is now widely used.
In my experience TypeScript's unsoundness is a feature, not a bug. You cannot compare it to languages that were designed from the ground up to be sound. JavaScript was fundamentally not designed to be used in a 100% sound way, and trying to do so introduces an enormous developer burden in terms of idioms and language features that you can't use, and syntactic hoops you have to jump through. You find yourself constantly fighting against the language.
I know this because at my company we started with Flow, which is (very proudly) sound. It was impossible to be productive. For a small example, every reference to document.body had to have a null-check, even though we could guarantee by where we placed our <script> tags that the body would never ever be undefined. The experience was rife with situations like this, which created tons of cruft and made code harder to read.
"any" is nearly always bad. But the other escape hatches, like casting, have proven essential to building a reasonable codebase that doesn't try to ignore the reality of JavaScript.
> Runtime type checking would be beneficial when dealing with JSON payloads returned from API calls, for example.
This I agree with, but I don't think it belongs in the core TypeScript project. We used Flow Runtime with Flow (https://gajus.github.io/flow-runtime/) which creates runtime assertions automatically based on your static types, on a file-by-file basis. It was wonderful, and is the only thing I miss about Flow, although it was a third-party project and there's no reason something similar couldn't be made for TypeScript. I would be giddy to hear that someone was building one.
These kind of articles seem to be commonplace and I frankly do not think they provide any productive value. "any" is supposed to be the escape hatch as TypeScript is designed to be incremental. Anyone with a semblance of understanding knows that TypeScript transpiles into regular JavaScript, you don't run TypeScript code in the browser.
Can you please provide more detail when it's best to use runtime type checks? For webapps with strict TS config, I'm failing to see the advantage. Runtime errors can occur when crossing boundaries from server API to UI but when you discover the exceptions, you go fix your contract.
This example from the article looks like an argument for immutability to me.
interface A {
x: number;
}
let a: A = {x: 3}
let b: {x: number | string} = a;
b.x = "unsound";
let x: number = a.x; // unsound
a.x.toFixed(0); // WTF is it?
No, but not because the tired diatribe on typing, but because JavaScript nowadays is mostly good enough... As long as we remember what its goal is and we don't use it for anything more than its intended purpose.
That aside, everything in that post re soundness vs unsoundness was what I used to know (and was taught in college) as "strong" and "weak" typing, is the redefinition recent? Was I taught incorrectly? That aside, C is weakly typed (and there are long, long discussions on it) but I'll say that, if it's good enough for C, then being for or against it becomes pretty much subjective (obligatory IMO, my saying that at all is also subjective).
After all, it's a flamewar that's been going on for decades. Typescript certainly isn't going to solve it.
Sound and unsound aren’t the same as strong and weak. Weak and strong systems aren’t honestly very well defined, but under many definitions can be both sound and unsound, though the theory of the weak system will need to have explicit inclusion of the implicit casts (e.g.).
Type systems become unsound when they, as a total system, cannot uphold their own promises. The promises of a weak system are weaker, but not (always) non-existent. In an unsound system you can generally, through trickery, take any expression reducing to a value of some type and modify it so that it appears to reduce to any other type at whim.
> Weak and strong systems aren’t honestly very well defined
I see, but at least the literature I read on typing actually mentioned them. Nothing I read in college regarding type systems ever included the words "sound" or "unsound".
> some type and modify it so that it appears to reduce to any other type at whim.
I saw instances of this as examples of "weak" typing.
Do you happen to have papers or books you could recommend so that I can learn more on type system soundness? This is a subject that I find really interesting.
How were weak and strong defined in the literature you read?
Anyway, more on soundness. I can recommend a couple papers:
Wadler (inventor of the monad) wrote a paper called "Well-Typed Programs Can’t Be Blamed" [0] about soundness.
There was a recent paper that made the rounds about how java's type-system is provably unsound as well that's worth reading [1].
I second the parent comment that weak and strong are ill-defined in my experience, while sound/unsound have well-accepted definitions and are commonly used in academia. Your experience obviously differs for whatever reason.
You can find many other references to soundness in the citations of those papers, as well as scattered around the internet.
As to if you think TypeScript is worth it, of course its worth it. Whether or not you're writing JavaScript or TypeScript, you're still thinking in types, function signatures and data structures. TypeScript allows you to encode that information so you don't need to hold it in your head (or have other programmers figure it from usage, as would be the case for JavaScript). But that aside, TypeScript's tooling alone makes it worth it.
As for this flame war, i strongly suspect that most type system advocates have mostly likely used BOTH dynamic and static languages (C#, Java programmers have had to deal with JavaScript for decades), while I expect the majority of dynamic advocates (say Python, PHP, JavaScript) have primarily stuck with dynamic.
Those with the wider perspective have the better insight imo.
JavaScript is very strongly typed. Everything will have a type. And it will be enforced and checked during runtime.
This will prevent buffer overflows and memory leaks.
JavaScript is dynamically typed.
This allows you to to do funny things like [1,2,3] + ",4"
Because it's strongly typed, all values will be converted to a type.
JavaScript does type checking during compilation, but it's not considered a static language, because it allows "duct typing" meaning you can add or remove properties and methods on objects during runtime.
I am aware of those definitions. It was "sound" and "unsound" what threw me off.
> Whether or not you're writing JavaScript or TypeScript, you're still thinking in types, function signatures and data structures. TypeScript allows you to encode that information so you don't need to hold it in your head
Static vs dynamic, a flamewar that's over sixty years old to which I am not going to contribute.
> But that aside, TypeScript's tooling alone makes it worth it.
JavaScript has pretty good tooling too.
> As for this flame war, i strongly suspect that most type system advocates have mostly likely used BOTH dynamic and static languages (C#, Java programmers have had to deal with JavaScript for decades), while I expect the majority of dynamic advocates (say Python, PHP, JavaScript) have primarily stuck with dynamic.
Should we ask LISP developers? How about its derivates like Clojure or Scheme? There are many of them here in HN (in fact, I've dabbled on Clojure a little bit myself), though that would probably lead to a different flamewar entirely (functional vs oo).
In any case, I don't follow what point you were trying to make writing this suspicion, it was clear that you were going to get counterexamples and you didn't provide sources that at least gave support to having such a suspicion. I would appreciate if you elaborated what was your implication.
By the by, I worked in java during 2013 and 2014, and I've been working with Go exclusively for nearly a year now. I still don't really care about what type system any given language I've worked on has, there's good and garbage code everywhere. Though, since all this can only be opinion until someone conclusively demonstrates a given type system's superiority, I still prefer writing python code.
> Those with the wider perspective have the better insight imo.
Maybe we should ask Guido van Rossum's perspective.
I believe most of the best in class tooling for JavaScript borrows on leveraging TypeScript compiler for inference. And its still below the bar offered by TypeScript.
> Should we ask LISP developers? How about its derivates like Clojure or Scheme? There are many of them here in HN (in fact, I've dabbled on Clojure a little bit myself), though that would probably lead to a different flamewar entirely (functional vs oo).
OOP is not at odds with FP. They are different things entirely and can be leveraged in equal measure within a codebase. Consider C# and LINQ (with LINQ derived from lazy expressions in Haskell). As for LISP, TypeScript services as a good test case for layering dynamic languages with type systems, so why not TypeLISP?
Anyway, the point im trying to make is, irrespective of if the programmer is working with a type system or not, the programmer is still reasoning about software with types. The dynamic language programmer is still thinking about function signatures (arguments, return types), they are still thinking about 'the properties of some object' and they still think about generic abstractions (as in .map()). I don't understand why JavaScript programmers are adverse to encoding that information in a type system when it solidifies and communicates their intent. (both to other programmers as well as the compiler)
JavaScript on its own requires the programmer to infer the original developers rationale from usage, and assertions of 'correctness' can only truly be inferred by running a software (by test or otherwise). Obviously, both TS and JS need tests, but in the TS case, you've removed a whole class of issues around types and call signatures, where as in JS, one might be compelled to test both 'types' and 'logic'. A type system can at least narrow huge classes of problems, allowing a programmer to focus on testing logic, not the inadequacies of their (or others) brains to hold mountains of implied type information in their heads.
If you want one practical example..... refactoring. While JavaScript is dynamic, just change the name of a function, or move functionality elsewhere...what assertions can JavaScript or its tooling provide that all dependent code is appropriately updated as a result of that refactor?
Honestly, choosing to leverage of a type system, imo, is an open admission of the complexities of software, and the fallibilities of the human brain. Rejecting the benefits of a type system to me demonstrate a dangerous over-estimation of one own abilities, or a lack of personal introspection with respect to reasoning. While this debate seems to continue, I don't think my views on it ever will.
> As for LISP, TypeScript services as a good test case for layering dynamic languages with type systems, so why not TypeLISP?
Lisp users think that a 'dynamical' and a 'dynamically typed' language are two different things. Lisp is both. Being 'dynamically typed' supports or simplifies a lot of dynamical features.
There are a bunch of languages which implement a subset of Lisp features and which were adding type systems. Historically much of typed FP was developed out of Lisp tradition.
Common Lisp for example has CLOS definitions like:
(defclass person (living-thing)
((name :type string)))
So the definition makes clear that are a class/type which inherits from the class/type living-thing. It also defines that contents of the slot name are of type string.
A CLOS method then is:
(defmethod say ((from person) (to person) (m message))
...)
Which means that an object of class person sends to another object of class person an object of class message.
Multi-methods in CLOS allow us to define and dispatch on the class of several arguments.
Common Lisp then also has type declarations:
(defun collide (a b)
(declare (type moving-object a b))
...
)
This allows programs to document types, compilers to use types for optimization purposes and some compilers to do type checking.
What Common Lisp lacks is a more extensive/expressive type system which would be competitive with what Haskell/or similar would offer.
I'm a bit shocked that you haven't seen a discussion on the "virtues" of functional programming and "evils" of object oriented programming (and vice versa) here. If you truly haven't seen it, you might soon enough.
> Anyway, the point im trying to make is, irrespective of if the programmer is working with a type system or not [...]
Everything after this is one of the arguments in favor of static typing, and I've read variations of it for years. There are just as many, and just as old, counterarguments.
I wish there was a way to conclusively prove which type system is truly better, if only to put that discussion to rest and for the industry to converge upon that type system. And then we'll be able to focus on other things.
Until then, this is mostly a matter of opinion, which is why...
> I don't think my views on it ever will.
... Neither will almost anyone else's, there's no absolutely compelling reason to.
You've shown that one can give a black-and-white definition of soundness and have it be useful.
That doesn't mean that there's no way to give useful meaning to "more sound" or "less sound".
Consider the following family of languages:
L has a sound type system.
LD (D for "dynamic") is L plus the ability to escape from it, in specially marked "unsafe" sections of code, into a dynamically typed system. (So evaluation can produce runtime errors, but they can be handled safely.)
LC (C for "crash") is L plus the ability to escape in "unsafe" sections into a system like Ld's except that now type errors aren't checked for and caught at runtime, they just produce memory-stomping and buffer overflows and the like.
D is just LD's "unsafe" language.
C is just LD's "unsafe" language. (Any resemblance to actual real-world languages named C, living or dead, is purely coincidental.)
The only one of these languages that is sound is L, of course. But there is some quality closely related to soundness by which there's an obvious partial ordering where L > LD > D, L > LC > C, LD > LC, and D > C, and if someone describes this situation by saying that LD is "sounder" than C then I don't see any reason why we should stop them.
Similarly: a federal state is either a "perfect union" or not, for any reasonable definition of "perfect", but it's reasonable for the US constitution to aim at a "more perfect union". A thing is either "unique" or not, but despite the cries of pedants it's reasonable to describe a limited-edition Ferrari of which only two were ever made as "almost unique" and as "more unique" than a standard-issue Ford Fiesta. A given region of space is either "vacuum" or not, but most of the things we describe as "vacuum" actually contain some very rarefied gas and it's common to use terms like "high vacuum" to quantify how vacuum-like a "vacuum" is.
I've found TypeScript and React with VSCode to be incredibly productive for web development. If you're on the fence, give it a shot.
TypeScript is a superset of JavaScript, so it brings a lot of baggage along with it, but if you're writing frontend code it's a real pleasure to work with compared to JS.
There is quite a lot missing in TypeScript types (e.g. integer or uint types would help a lot in typical tasks such as accessing an array or iterating a loop; even if they are stored as floats, I want to make sure someone won't take 2.5 element of a list to get undefined).
Still, for me, TS is a day and night difference with JavaScript. While it does not solve everything, at least I know what is code about. E.g. when I see `volume` in plain JS the first question I ask myself is: is it a number, plain object, instance of a class, a simple boolean, or what?
It is not only about tests. It is also about readability.
Typescript from the ground up is absolutely worth it, starting with how it gives you full IDE inference for all those "big blob of options" objects that a lot of JS methods use.
I agree, and I've found that WebStorm (and other JetBrains software with JavaScript support) does a great job at it, although I do find TypeScript improves the experience.
I don't believe many understand how much tsserver is doing behind the scenes in Vscode for JS code bases.. Of course any IDE can do that, because they can use tsserver via the language server protocol haha.
Ignoring the tech side for a minute, my major problem with TypeScript is that TS proponents assume that the static vs. dynamic debate has been settled. That the future is static typing, and that people have to be crazy for wanting to do dynamic typing in a dynamically typed language.
A great many developers have found themselves having to write JavaScript because that's where they got pigeonholed. Then, because they'd rather be doing Haskell, Java, or whatever, they bring that crap to JS and enforce it on developers that possibly liked doing regular JS.
Type inference is older than I am. Yet you would think this is some new technology. When in reality, we could have had it on Ruby. On Python. On Perl. And on Lisp. Decades ago. It's not new and it's not special. But it certainly has a lot of undeserved hype at the moment.
There is no conclusive proof static typing reduces bugs. This is some weird myth that keeps circulating. The trend pendulum will continue swinging though. TS won't be immune.
I don’t see how you can claim that TS believes the static vs dynamic debate is settled when TS already includes, and continues to add features that enable and make it easier to dynamically type stuff (for example, unknown is a fairly recent addition).
In reality, the reason TS has been so successful, and likely will continue to be so successful (until they screw up the execution, or forget what made the language so successful) is thst TS is an entirely pragmatic language. It uses theory not to decide its roadmap, but rather, uses actual problems and needs faced by JS developers to decide the roadmap, and then uses type theory and other CS concepts to solve those problems.
For example, the reason TS uses Types is not because a group in MS decided that static typing is better than dynamic typing, and what JS really needs is a layer of types above it (that was coffeescript...). Typescript was created because MS wanted to make better IDEs and tooling for JS (specifically, they wanted to bring Intellisense which is a huge VS selling and marketing point, to JS) and they realised they couldn’t do that without adding type information to JS.
Hence, a practical need drove the decision to create TS.
IDEs aren’t that necessary if you have strong command line skills. The only ide feature I really miss is autocomplete because it’s a pain to remember method names
So the IDE stuff isn’t extremely advantageous for me.
But that’s the reason Typescript was created.
A convenient side effect of that for me is that I can manually use the same clues an IDE used to understand the code base I’m working on easier, faster and with more confidence than I would be able to in a vanilla JS project.
Ignoring runtime bugs, static typing and type hints gives me (a) autocomplete that works better (b) near-real-time linting that lets me catch dumb bloopers before my focus shifts to a different zone (c) lets me inspect objects/signatures and get up to speed on other people's code way faster than having to look up docs.
This alone gives me a huge productivity boost and I can't imagine working without it. It just so happens that anecdotally I see runtime errors inversely frequently to my discipline with using hints (go > python w/ hints > python w/o hints).
I like having some wiggle room for rapidly hacking stuff out. interface{} and Any are nice in small doses. Namely as parameter types and only when I'm the only one using the code and just need to ducktype my way through something.
> the adoption of TypeScript would not have been as high if a sound type system is 100% guaranteed. This is disproved as the dart language is finally gaining popularity as Flutter is now widely used. Soundness is a goal of the dart language which is discussed here.
Look, I wish TS type system was sound too, but that's not a "proof", it's just a data point. TS doesn't have the same goals as Dart, there are lots of variables which aren't the same, so you can't just take Dart's approach and say "look I proved it". There are lots of popular languages with unsound type systems; Java for example.
I don't know how you could have a criticism of TS without mentioning the lack of sum types. Yes, you can fake it by having an object with the same field name differentiated by a string constant, it's still not as nice as having first class support for sum types.
Another Typescript fan here. The title didn’t match the post content but I agree with everything he wrote concerning type “soundness”. From my POV it’s a downside of typescript but it makes sense since it’s just a superset of JavaScript
Yep, it ain't perfect but it is so much better than not having it. Anyone being concerned about TS adding overhead please take a look at how generics in Java work - both struggle with erasure of type information at compile time. Java may be sound but doing anything non trivial with generic types gets complex quickly. Compared to Java generics doing TS advanced types is almost a joy.
I might get downvoted, but I believe that using "strict" mode is not worth it precisely for the reasons this article talks about. You get at least 90% of the benefit of Typescript (IDE support, typos, refactoring, etc) without --noImplicitAny and other flags that induce a lot of boilerplate and wasted time. After all, how confident can anyone be that DefinitelyTyped types are correct? I use types when I find them useful. I also enjoy being able to not use types when I don't need them, which is why I choose to use Javascript.
I won't lie. I read the title, and thought "At last, objectivity". It's like CoffeeScript all over again.
Except this time, this particular assault on ECMAScript is much better funded.
Don't get me wrong, I think there's a place for strong typing in web based UIs. But for one, WASM is a better idea for the choices and flexibility it offers, and more importantly to me, I think the bigger challenges in UI programming are not solved by strict typing.
Most npm modules are written in JavaScript and when it comes to types they are handwritten (if they exist at all). The very nature of this makes TS unsound, as you have to count on the types being written correctly. I've encountered incorrect types many times. Even worse, when the types ship inside the module and are incorrect, it's difficult to correct them. I prefer JS modules to have a separate @types module.
If you’re writing JS, then adding TS will give you a lot more reassurance when writing new code or refactoring old one. Even if it’s not perfect, I don’t see a reason why you’d choose JS over TS today other than maybe you don’t want to go through the first month of getting used to. Once you’re out of that, you’ll end up with code that’s much easier to reason about and also debug, in my humble opinion.
In general it seems hard to be in-between dynamic typing and static typing (compiling). As the article implies, one typically has to do more testing if dynamicness can allow "bad data" in.
The trade-off for dynamicness is more productivity and (potentially) easier-to-read code because less code is needed on average to express an idea since type-related code isn't needed as often. But if TypeScript is not type-safe enough to reduce the need for fine-grained testing, then you get the worse of both worlds: the verbosity of types and the busy-work of micro-testing.
That being said, I wish JavaScript would add a feature to allow optional parameter checking such as:
function foo(a:intReq, b:datetime, c:datetimeReq, d) {...}
Here, the "Req" ("required") suffix means the field can't be null or white-space. Parameter "d" has no type indicator. It's still "soft" type checking, but would catch a lot of problems earlier.
I've done some programming with typescript. IMHO the reason for around 50% of the javascript world now using it is that it provides substantial improvements over javascript that are real. Personally, I insist on typescript on any node.js or frontend javascript codebase these days.
Yes, it's not perfect. And, yes the inherent problems of javascript still leak through in many places. But with a little configuration, you can make the static code analyzer and compiler help you avoid most of that.
IMHO the amount of verbosity it adds is negligible. I actually measured a slight decrease in LOC when I was converting code. You add some type annotations (which IMHO also help human code readers) but then you also get access to syntactic sugar to offset that. It's a fair compromise considering that most tools trying to help the developer have more to go on and basically work much better. Ballpark your number of LOC is not going to grow when switching to typescript.
But IMHO it's just a gateway drug for developers to find their way to more capable languages. Personally, I'm interested in the direction that Jetbrains is taking with Kotlin-js, which with the next version will have some nice tooling around it to leverage typescript type definitions for integrating npms and hides most of the build tool madness behind gradle. The kotlin compiler has a much more sound/strict typesystem than typescript and also the language has quite a few language features that the typescript developers have not gotten around to supporting yet (they've been making progress catching up with recent releases though). They are also addressing size of the minified code. With the recent improvements, hello world is now a lot more reasonable than it used to be (<80kb instead of ~1MB). It eliminated unused code more effectively.
If you feel brave, you can get started with this on a modern react project right away. Most of the dependencies you'd use for that should just work fine with kotlin-js. But realistically it's probably going to take another few years to stabilize. IMHO Kotlin is interesting because it is already widely used by frontend developers on Android and has a lively ecosystem of frameworks that might also make sense in a browser. Also co-routines are nicer than async await in typescript.
And of course WASM is opening up this space further to just about any language. So for people finding themselves a taste for more/better typing, there will be plenty of alternatives for typescript.
It's interesting that someone who is primarily using JavaScript in the backend is criticizing TypeScript's type system. There is a simple reason why TypeScript's type system is how it is... TypeScript is additive which works well in practice.
LogRocket deserve some praise for doing the company blog right. Good, self hosted content (no Medium) and well written pieces on technology from a practical everyday perspective. I am not a customer, by the way.
This article is wrong from many aspects. There are things that many developers can't get with Typescript and it's flexibility...just always keep in mind what makes Typescript so special compared to any other languages. You can't achieve both flexibility and typed solid programming in the same place, you must sacrifice the things you just mentioned and few others. Typescript was supposed to be flexible and developer friendly, it's not a hardcore language and never will be.
Their example without `: number` still exhibits the exact same behaviour.
The `: number` isn't a cast either, it's declaring the type of the variable.
You can't help but explicitly name the type of a variable in many cases. What if they were passing val to a function as an argument? You can't have 'function foo(x)', you have to have 'function foo(x: number)' and name the type.
So yes, their example is contrived, but no, what you said is not sound. It has the same problem as the one the author mentions. The typescript compiler intentionally does not catch their issue.
If I'm misunderstanding what you're trying to demonstrate, please make a typescript playground of it.
I think I’m alone in this, but JavaScript is a neat language that is incredibly powerful and expressive (minus a few warts). Most of the problems that arise from it seem to come from undisciplined work habits and a lack of code review. As long as your team is smart enough to not call all your function arguments “args” or “data” or “params”, the asynchronous flows are really nice. IMO at least
I don’t know how these people are using TS, but I’ve been part of a major project for over a year and we’ve had zero runtime issues, using all kinds of libraries in both nodejs and the browser. But for our standards, that object assignment in the example is a big no-no, TS or no TS.
It's worth it because it catches a lot of low-hanging bugs at a small cost, and it's well maintained and has great tools support. Plus you can even use it like a simple linter if you want to continue writing vanilla Javascript (it catches less, but still catches a lot).
Most machine learning doesn't work the first time and required rapid iteration outside of your production system in my experience. Then, when you finally get a model, you can put those weights into your stable production system that has all the automated testing and is written in a typed language.
nope - the loss in velocity is not worth using it unless you're a massive team. TS is for Java programmers who are mad at Javascript for existing and don't know how it works well enough to grok overloading etc. plus you get a lot of the benefits of TS in an IDE like VSCode without actually having to use types at all. if you're a javascript native, typescript is constricting and eliminates one of the best parts of javascript - no types!!
I think this depends on where you are measuring costs in your codebase. If you're strictly looking at time to market on your first iteration, then you're right, types are going to require more of your team's time up front. If you look at the total lifespan of a "real project", then type safety is likely going to represent a cost savings regarding time your team spends on bug fixes, refactoring, and maintaining legacy portions of your app.
For context, I base the above sentiment on our team's last two years of effort in migrating key parts of our app to TypeScript (25 devs, ~500k lines of code). We've found that it has substantially reduced the number of defects that make it into production, and it has also reduced the number of round trips through QA. Clearly, this is only one data point, but hopefully it offers some perspective on why TS can be valuable in a codebase.
sounds like its working for you, so i'm glad. you're about what i'd consider a big team / codebase. i can't recommend TS to people trying to create product from nothing. if your startup is doing well, you can hire someone to annotate your code w/ types later ;)
My experience is the opposite. One of the best things about TS is yhe refactoring experience - change a function signature for example and TS will tell you all the places you need to update. In a fast moving greenfields project with various requirements in flux, I’ve found this invaluable
I agree on this too. Oftentimes, we'll change a type and then just follow the errors in VSCode. It's a pretty great way to track down all the codepaths that are affected really quickly.
I'm not quite sure why you consider "no types" to be one of the best parts of JavaScript, but TypeScript certainly doesn't require the use of types. It just makes the functionality available.
TS/JS don't allow for overloading the way that other languages do - it does not allow you to differentiate between functions via number of parameters, only types of parameters. If you try to differentiate based on number of parameters, it will simply use whichever definition of that function name it finds first.
// add.js
function add() {
switch (arguments.length) {
case 2:
return arguments[0] + arguments[1];
case 3:
return arguments[0] + arguments[1] + arguments[2];
case 4:
return arguments[0] + arguments[1] + arguments[2] + arguments[3];
default:
throw new Error("Too few or too many arguments. Number of arguments: " + arguments.length);
}
}
module.exports = { add };
JavaScript has types. typeof {} === 'object'... Sure, they're very weakly held, which can be a lot of fun when you accidentally add a string and a number, but variables have types.
In type systems there's a tension between expressiveness, soundness, and comprehensibility. A sound type system must exclude all programs that have runtime issues, and, holding that constant, try its best to maximize expressiveness and comprehensibility of the programs that can be written.
But what about programs that can run fine but are excluded by the type system? They are censored. This is what's less visible until you actually try to build something substantial, pushing the boundaries of expression, performance, or scale. Then you'll find that a sound type system can become more and more of a straight-jacket. One that forces you to write code in ways that are limited by what its type designers could envision expressing (or could prove that was safe to express).
The trouble is that it's hard to know when you might reach the point where the type system begins to limit you. It might never happen, as you frolick happily within the walled garden. Or it might happen when you write your first line of code. In a large system that must deal with external requirements, it's something that in my experience becomes inevitable. That's not even touching upon comprehensibility, which becomes an increasing challenge for purely sound type systems as they attempt to increase expressiveness.
The biggest innovation of TypeScript is JavaScript. That is, TypeScript started with a huge established corpus of JavaScript that showed what developers wanted to express and how they wanted to express it. And, crucially, how popular various kinds of expression actually were in the developer community. This forced TypeScript to take practical expressiveness of millions of lines of JS seriously, that ordinarily would have been censored by type system design before the first line of code was written.
That's why it's entirely unfair to harp on TypeScript's unsoundness, without also exploring the corresponding gain in expressiveness and comprehensibility relative to other type systems.
BTW, this line of reasoning is the reason so many "sound" type systems also have their equivalent of "any": Rust has unsafe[1]. C# has unsafe[2]. Flow has any[3]. And all practical languages have strings, which are kind of a lowest common denominator when it comes to dealing with a type system that simply can't express what you need.
Moving an existing codebase to it is a giant pain, otherwise it's not bad.
It fits you into better practices, but don't treat it as types are always true. When debugging issues I'll commonly assume Typescript's types do not exist or how it would act if they are wrong. This is notably true of consuming API responses with mixed data types.
Generally, what I really like about typescript is that you normally blend it with linting, minifying, and strong IDE integrations. These things, when combined, really help code readability and adherence to TS conventions and eventually save you from massive bugs that might not have been caught prior. Obviously, at the end of the day, how you use it really defines how helpful it is.
All that said, I haven't ever really had major issues with the tools around TS.
When I introduce it to a new team, leaving js/jsx as valid and making TS optional is usually good enough. Whether through peeking at your TS code or just being curious, usually they come around, and if not, they are still responsible for their code. The tradeoff is theirs to make
I don't like TypeScript at all. I used to write c# and javascript all day. Every time I had to write javascript, it was much more enjoyable than writing the c#. I would often have to create classes purely because the language required them. All of these classes all over the place with no methods in them at all. A whole bunch of typing all over the place that I could tell I was never even mentally parsing, but still having to type and see on the screen.
Then I started working on my own and decided to use Node a lot in the situations where I would have to use c# at my previous job. There were no more bugs, no problems with refactoring, and it was much more enjoyable than writing c#.
Now this TypeScript is infecting javascript, and all the jobs and so on require "typescript" instead of javascript. It puts all the things I didn't like about c# back into javascript. People will make all kinds of ridiculous claims that it is irresponsible not to use TypeScript, and that it improves productivity and so on. None of these claims are true and don't get to the real reason that TypeScript exists.
Most well-structured programs aren't one giant function that is hard to refactor. They are usually a series of isolated, small functions. There is even a trend towards "microservices" and function as a service and so on. These are things that reduce the claimed reasons for TypeScript, rather than increase it. Many times I see the reason for using TypeScript being "it is good for large teams". Yet still, I see it used in teams of 3 people all the time.
The real reason TypeScript is used is because there are a very large number of programmers now, which means the quality of the average programmers is much lower than it has ever been. Many people who program often just copy and paste code from stack overflow. Many of these people were the same people who looked in fear at JavaScript, and couldn't even understand it because it wasn't the one language that they knew. TypeScript gives these people more safety, feedback, and familiarity to be able to do their job.
That is fine, but lets not pretend it is anything else.
Let me elaborate.
If you are starting a new project, yes use typescript over JS. And configure it with all strict options.
This doesn't magically give you well architected and well-tested code, it still takes a good engineer to do that. But what it does allow is for the engineer to refactor the code to support new functionality with more speed and confidence, rather than shoehorning it in in fear of breaking something or it taking too long. Furthermore, as people join your team, it will be a lot easier for them to figure out where some piece of data is coming from and how the code flows from one spot to the next.
If you have a massive JS project and you want to convert it to typescript. The answer might be no. Oftentimes when people port a JS project to TS, they end up using non strict typescript, and you get the syntactic overhead of writing TS, without really any benefit because nearly everything is any typed. If your only concern is a better codebase, and it doesn't matter how long it takes, then sure it is worth it, but we all know that is never our only concern. It still might be worth it, but I can almost guarantee you from experience, you will still end up with TypeErrors in your console at some point, because people will have just any typed things.
Also if you're writing a tiny node script in a single file, to do some simple operation, then no it is probably not worth setting up a command to compile it and a tsconfig to configure it. Unless it is doing multiple things, and/or someone else in the future will have to use it, then it is probably still worth it.