Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

What is unnecessarily complex about TypeScript? It's JavaScript, with static typing plus type inference, and pretty nice generics. The ecosystem of modern JS surrounding it is horribly complex but TypeScript itself seems like a fairly straightforward programming language.


It hasn't been a big detriment for me as someone learning Typescript on their own, but it is another moving target for looking up "how do I do x..." and finding most of the forum posts are a little outdated and the latest version of Typescript has a different/better way of doing things than just a year or two ago. I find myself scrolling through github issues comparing my tsconfig to figure out why my stack behaves differently than someone else.


That was my experience with TS maybe two years ago - at this point project scaffolding tools are good enough to generate sane output that I spend a little bit of time upfront but then keep plowing away. Maybe I got better at it as well - but I haven't kept up with TS news in a long time and I don't feel like I'm missing out on stuff or encountering things I don't understand.

I've written >50k LoC of TS in last few months for sure (doing a huge frontend migration for a client) and I can't remember the last time I googled anything TS related. Actually I remember - a month ago I wanted to know how to define a type with a field excluded, took 30 seconds of google.

Meanwhile the project started out as mixed TS and ES6 because most of the team was unfamiliar with it and there are a few dynamic typing evangelists - we ended up going back and just using TS all over the place, the complexity introduced is minimal and the productivity boost from good tooling on product of this scale is insane.


Typically for me the time cost is in going down rabbitholes to attempt to improve implicit static types for getting closer to "whole program" functional type inference (TypeScript repeatedly seduces me into this), and the decision inflection point is generally not for application code but for the space between application and script code, things that you might also write Perl or python scripts to accomplish...the types are especially useful in this context because they tell you a lot more about the script than your typical script, but they also introduce a bunch of overhead for a few lines of code.


Yeah I probably shouldn't complain, I've written less than a couple thousand LoC so I'm still googling a lot, but TS has definitely paid for itself in code clarity already.


Did the dynamic typing evangelist eventually agree to noImplicitAny?


> and finding most of the forum posts are a little outdated

Which is why you use the up-to-date official documentation.


There is complexity in the type system with signatures like

``` function partialCall<T extends Arr, U extends Arr, R>(f: (...args: [...T, ...U]) => R, ...headArgs: T) {

`


At least I can pick it apart and deduce it. I can even use the compiler to give me information about it via editor integration (all same editors have it now). So I’ve I’m unsure it might take a few minutes of investigation to completely understand the signature.

I’ve seen some JavaScript written so tersely it was nearly impossible to figure out without spending Possibly hours on unwrapping the code. That’s the value provided here.


When it comes to spending hours unwrapping code, gotta love js that has undocumented heavy use of string based property access for things like imports. Like the worst of both functional and OOP combined, and generally no IDE support to be had.

Implementation approximate, but otherwise true story, both LHS and RHS:

`let { statefulMutatingAccessor } = require(obj["prop."+tree+".subtree"])`


It's funny that you consider that complex; I consider that a pretty normal, expressive type signature that gives the compiler critical information about how I expect to use that function, which in turn allows it to completely squash several classes of bugs I might write.

But I love strong type systems; people who prefer weak type systems would likely consider things like this to get in their way.


Could you elaborate on this point for someone who isn't familiar with typescript (or javascript, for that matter). I'm no stranger to strongly typed languages but that function signature seems pretty complex to me. By elaborate I mean explain what's going on in that signature, and what the critical information it supplies is?


It's from the TypeScript 4.0 beta blog post[0], which describes it as:

> partialCall takes a function along with the initial few arguments that that function expects. It then returns a new function that takes any other arguments the function needs, and calls them together.

The type signature looks like:

  type Arr = readonly unknown[];
  
  function partialCall<T extends Arr, U extends Arr, R>(f: (...args: [...T, ...U]) => R, ...headArgs: T) {
First of all, know that in TypeScript, colon separates a variable or parameter name from its type. So in C or Java you'd say "Date lastModified", in TypeScript it's "lastModified: Date".

Now, looking at it piece by piece:

  function partialCall
declares a function named partialCall.

  <T extends Arr, U extends Arr, R>
says that this is a generic function with type parameters T, U, and R (the first two of which must extend Arr, that is, a readonly array of objects whose types are unknown).

This function's first parameter is:

  f: (...args: [...T, ...U]) => R
The name of the parameter is `f`, and the type of this parameter is `(...args: [...T, ...U]) => R`. This means that `f` must be a function whose return type is R, and its parameters must match `...args: [...T, ...U]`.

The `...` in `...args` makes it a rest parameter[1] (variadic parameter in other languages). The type of `...args` is `[...T, ...U]`, which you can think of as the concatenation of the T and U arrays. (It's a bit strange to see `...` in a type specifier, I can't recall having needed this before.)

  ...headArgs: T
says that the `partialCall` function itself is also variadic, and its arguments are gathered up into headArgs. The type of headArgs is T.

I don't know if I'd call this normal or clear compared to the signatures I encounter daily, but it's pretty elegant for a higher-order function that takes a function with any signature, and some arguments that match the given function's parameters, and returns a function with the matched parameters removed. And the implementation of partialCall is just this one line!

  return (...b: U) => f(...headArgs, ...b)

[0]: https://devblogs.microsoft.com/typescript/announcing-typescr...

[1]: https://www.typescriptlang.org/docs/handbook/functions.html#...


Thank you for taking the time to explain that, it is much appreciated.


I would say, there is some complexity in reading (and writing) the thing. But there is probably not as much complexity in using it. And you are explicitly codifying the complexity in one place, that would presumably still exist in a dynamically typed language, just implicitly, and likely spread out across the code. This looks pretty nice to me at a glance, just like anything once you write a few of them and come across a few in the wild and take the time to pick them apart they no longer seem so scary.


Offtopic but what's nice about Typescript generics? Typescript doesn't even let you specify variance. It's one of the unsound parts of the language's type system in fact.


You cannot explicitly specify covariant vs contravariant, but TypeScript absolutely does allow you to express these relationships. Unless I misunderstand you.

That said, the type system has come a very long way even in just the last year. The biggest improvements imho being around recursive types (which was one of the biggest holes for a long time imo), literal types / literal type inference, and tuple types.

It's not complete by any means, but it's improving quite rapidly.


It appears that function parameter bivariance is still a thing? [1] Although there seems to now be a flag to make this one use of variance correct.

I would assume even Array<T> is still bivariant as well...

Both of those are horribly unsound, just for convenience. Sure convenience and compatibility are Typescript's ultimate goals, but to actually praise it for its generics? That's very strange.

> TypeScript absolutely does allow you to express these relationships

How would you express a class with covariant or contravariant type params in Typescript?

[1] https://www.typescriptlang.org/docs/handbook/type-compatibil...


It is not the most feature-rich generic system but it's come a long way since the earliest revisions and does have some niceties: https://www.typescriptlang.org/docs/handbook/advanced-types....


describe the steps to release the simplest ever code in javascript to production: write a js file, host it, done.

The same thing in TS adds at least one step (not to mention the rest of the tooling you will want)

So while a prefer it over JS, there's no arguing that it is more complex as now you require a build step for a language that only exist because people wanted a language without a build step.


Almost nobody does Javascript without a build step these days, unfortunately. I miss those simpler days.


You might like deno (https://deno.land) then!

It’s native typescript.


Except for the core which was recently reverted to ES6 due to Typescript's slugguish compilation and inefficiencies.


I would say nobody at tech or hip company.

A lot of fortune 500 companies with some developers who missed the trendy stuff still do it that way. I made a medium size website (30 pages) in React with pure javascript and dependencies being script tags in index.html to vendored files.

So not even JSX. I did it that way because it was the easiest way to develop and deploy in that environment


If you don't need IE support and only care about modern browsers...

<script type="module">


And also don't use modern frameworks like React or Vue, or don't mind sticking all your templates in strings, or in your index.html, and shipping 100kb of template compiler to to user, or write render functions directly ala mithril.


my team (in a large enterprise) uses js for scripts using a shebang interpreter declaration, eg

``` #!/use/bin/env node

console.log("hello cli") ```

While it does depend on node, and there are arguably better crossplatform languages for this purpose, it is a zero-tool chain use case that is very convenient for us.


Yeah, fuck those guys. script tags or GTFO of my project!!


Now, that is a bold claim. Are there any stats on that?


"Nobody" here means few, or more loosely, much fewer teams than before, not "literally 0 people/teams".

And the group mentioned is (I deduce) not generally individual devs, enterprise devs building some internal thing, and so on but teams in companies doing public-facing SaaS, teams in startups, companies like Amazon/Google/Facebook/Apple all the way to AirBnB etc, and so on.

So, you don't really need stats for that.


Yes, exactly. The larger the team, the more likely someone is a front end expert and wants to use latest cool framework, which will by its nature require a build step. Even for something simpler, you'll probably want it for cache busting, minimization, etc.


Browsers should just bite the bullet and add TypeScript support.


At the rate the Typescript is releasing, that'd be a support nightmare. Perhaps a better solution is for TC39 to propose optional types. It could be modeled on Typescript for sure, but it would still be backward compatible.


Javascript of today borrows liberally from coffeescript of yesterday, so it would make sense for javascript of tomorrow to borrow liberally from typescript of today.


You can have that with WASM.

But then if that's an option, I think Typescript will be the last language I migrate to, because Typescript development culture, tending as it does towards overcomplicated solutions to simple problems, is unpalatable to me.

I'm drawn to the idea of using Rust over WASM as a frontend language, and I think I'd rather choose that approach to develop any browser UI where type safety is critical, provided there is no discernable difference in performance (when compared to TS over WASM).


Yes, it's probably a better idea to improve WASM than add a proprietary format (TS is by Microsoft) to the open browsers. Google tried to do the same thing with Dart and it was decried about a decade ago, so now they use it for Flutter.


I think it will be great once support is broad enough. It might, ironically, increase the current fashion for framework churn, but at least there will be no single language for developers to derride.

In fact, I wonder how ECMAScript will fare in a post WASM world... I suspect it would still thrive tbh. Or perhaps people will take to other flexible, expressive languages for UI development. Like Python's niche in computer graphics, or Lua in games and AI research.

I can still see myself using JS in that future. But not for everything.


Can't put my finger it for you, but it defn doesn't feel as good. Perhaps because it needs to interop with javascript, and js objects are all over the place with types.


It's the price of backwards compatibility with JS.

One could completely forgo that, and come up with PureScript or Elm. But then one can't beverage most of the existing code.

Same tradeoffs as what Kotlin and Scala make for Java's sake.




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

Search: