
Google Feedback on TypeScript 3.5 - dkns
https://github.com/microsoft/TypeScript/issues/33272
======
heavenlyhash
> (I might suggest the underlying problem in this code is relying on inference
> too much, but the threshold for "too much" is difficult to communicate to
> users.)

This is a _very_ outstandingly interesting line out the whole writeup.

I like the writeup in its entirety for being very balanced and thoughtful, but
this line in particular really stands out to me as worth more thought for
anyone interested in language and type system design.

Inference is great.

Except... when it's not. When it's "too much". When it starts making breaking
changes appear "too distantly".

It's an interesting topic to reflect upon, because the inference isn't
_making_ a breakage; just shifting around where the breakage _appears_. (And
this makes it hard to ever direct criticism at an inference mechanism!) But
this kind of shifting-around of the breakage appearance can be a drastic
impact on the ergonomics of handling it: how early it's detected, how close to
the important change site tooling will be able to point the coder, etc. That's
important. And almost everything about it involves the word "too" \-- which
means the area is incredibly subjective, and requires some kind of norm-
building... while not necessarily providing any clear place for that norm-
building to center around.

I don't have a point here other than to say this is interesting to reflect on.
I suspect the last chapter on type inference systems has not yet been written.
Can an inference system be designed such that it naturally restrains "too"
much use of it?

~~~
fluffything
IIRC there are a couple of Rust write ups about this.

In Rust, inference is limited within functions. The language doesn't allow
inferring the argument or return types of functions to avoid this kind of
action at a distance.

The function API is just one of the many arbitrary places where one can limit
inference and require users to provide types. In other languages the module
boundary might also be an appropriate place to do that.

~~~
cypressious
A big part of TS is the ease of transition from JS where you immediately get
benefits through gradual typing and return type inference is probably a big
part of that.

~~~
jchw
This is true, but a lot of projects are now starting with TS, and maybe could
benefit from intentional limitations on inference, at least locally. I’m
pretty sure most folks start new projects with strict enabled as is.

~~~
breatheoften
In my experience a lot of people don’t necessarily start projects with strict
mode — and they _really really_ should ... most people I’ve talked to who
couldn’t figure out how to use typescript never figured things out because
they ended up somehow with a compiler configured with noImplicitAny: false —
typescript really needs to find a way to change the default here ...

~~~
WorldMaker
Yes, my advice is almost always strict should be true for every new project,
and even sometimes for migration projects where the end goal is to absolutely
decrease tech debt and bugs as quickly as possible and the team isn't afraid
of a giant compiler error waterfall as motivation. (It's a nice auto-generated
TODO list with some semblance of progress reporting!) But at the very least,
everyone should do themselves the favor and "noImplicitAny: true" no matter if
it's new, migration, something inbetween, and no matter what they think of any
of the other strict flags. Explicit anys are searchable TODO markers and fine,
but implicit anys always seem to be hidden bugs waiting to be found.

------
burtonator
Typescript is absolutely amazing. I've been working with it for the last 8
months.

[https://getpolarized.io/](https://getpolarized.io/)

and the source is here:

[https://github.com/burtonator/polar-
bookshelf](https://github.com/burtonator/polar-bookshelf)

I could have NOT made as much progress just by using JS directly. When you
have a large code-base and you're trying to make progress as fast as possible
refactoring is needed and the strict typing is invaluable.

Honestly, the MAIN issue with TS are issues around webpack + typings for 3rd
party and older modules.

I'd say 85% of the code I want to use already has types but when they don't
it's frustrating to have to pause and write my own types.

I have 20 years of Java experience. Used it since 1.0 and for the most part
have been unhappy with everything else.

I've decided that Node + Typescript is by far the most productive environment
for me to switch to. I can code elegant apps with both front and backends and
I get strict typing.

Could NOT have made so much progress without TS.

~~~
patates
I'm writing this as a developer who writes a lot of C# and Typescript: Have
you tried C#? Even though I sometimes miss the flexibility of Typescript when
writing in it, I love the reliability. Maybe it's no _as_ battle-tested as
Java (especially with all the rewrites recently), but feels like it's 99.9%
there.

~~~
htgb
Not GP, but as a developer working with TypeScript and C#, I respectfully
disagree regarding the type system. There are many things I like better in C#
compared to JavaScript, but I feel overly constrained by the type system of C#
way more often, and most notable is the lack of discriminated unions (aka sum
types etc). You can say that something has this _and_ that, but not that it is
this _or_ that.

~~~
andrejk
I agree, as a mostly C# developer for years, then Typescript (Node, Angular
and React) for the last couple of years, when I go back to C# projects, I feel
like I'm doing a lot of work for the compiler. And really elegant use of the
TS type system doesn't translate well into C# many times, forcing me to write
boilerplate.

I've been eyeing F# for more elegant managed code, but that's going to take me
a little more up-front investment to get productive.

------
Stay_frostJebel
This write-up led me to Evan Martins blog. What a gold mine. Digestible
writing with a peek inside of Google's internals.
[http://neugierig.org/software/blog/](http://neugierig.org/software/blog/)

------
jannes
I love TypeScript. I started using it around v1.0. Microsoft has hit some gold
with it.

When I first started using it I had lots of `any` in my code (like the Google
employee is describing here). But over time it really starts being extremely
clean.

~~~
radicalbyte
I started around 0.7/0.8, it was already such a fantastic product back then.
It's just fantastic, Javascript on steroids.

~~~
Rychard
I don't have a significant amount of experience with TypeScript, but even from
my limited experience I agree that it's a fantastic product. That said, at
$JOB we (unfortunately) have a fair amount of production code written in
TypeScript 0.9 which nobody has ever upgraded, and simply won't compile on a
more recent version. It's been that way for _years_ now, and every attempt to
bring it up to date has been met with failure.

It may be an overly broad request, but I'd be very interested if anyone had
any suggestions for how we might go about performing such an upgrade in an
iterative manner. My understanding is that pre-1.0, TypeScript went through a
number of breaking changes (as would be expected of any pre-release software),
but I've never found a complete list of what those breaking changes were.

~~~
jannes
You can see a list of all changes in the "What's New in TypeScript" document
on GitHub:

[https://github.com/Microsoft/TypeScript/wiki/What's-new-
in-T...](https://github.com/Microsoft/TypeScript/wiki/What's-new-in-
TypeScript)

And a list of breaking changes here:

[https://github.com/microsoft/TypeScript/wiki/Breaking-
Change...](https://github.com/microsoft/TypeScript/wiki/Breaking-Changes)

They both go back until v1.1. For older changes you can check the blog:

[https://devblogs.microsoft.com/typescript/announcing-
typescr...](https://devblogs.microsoft.com/typescript/announcing-
typescript-1-0/) [https://devblogs.microsoft.com/typescript/announcing-
typescr...](https://devblogs.microsoft.com/typescript/announcing-
typescript-0-9-5/)

Anyway, it's hard to say what you need to do to get your code to compile on
the latest TypeScript version without knowing what some common compile errors
are in your code.

Here are some guesses:

1\. The way you obtain type definitions for third-party packages has changed.
It used to work with /// <reference> and tools like nuget or TSD (TypeScript
definition manager). But now it works with npm in the @types namespace and
some npm packages even include definitions themselves now.

Of course, updating to the latest type definitions would mean that you have to
upgrade to the latest versions of the third-party libraries as well. Otherwise
you have to find a way to keep working with the old definitions for the
respective library versions.

2\. Pre-1.5 TypeScript had something called "internal modules" which were
renamed to "namespaces" and are generally discouraged now. Hopefully your code
is not using that feature.

[https://www.typescriptlang.org/docs/handbook/namespaces.html](https://www.typescriptlang.org/docs/handbook/namespaces.html)

3\. tsconfig.json. I don't remember when they introduced it, but you likely
need to create this file and tweak the settings

~~~
WorldMaker
> 2\. Pre-1.5 TypeScript had something called "internal modules" which were
> renamed to "namespaces" and are generally discouraged now. Hopefully your
> code is not using that feature.

Related to this, the TypeScript pre-1.0 import/export syntax for "external
modules" was grandfathered in, but the semantics changed alongside the
"internal modules" changes in 1.5. So pre-1.0 import/exports still mostly
"compile" in post-1.5, but have very different semantics and that can often
cause a lot of downstream module shape headaches, depending (and exacerbated
by) the module format you are targeting.

A first pass making sure to rewrite all import/require/export statements can
save you a lot of work later, even though it won't directly cause compile
errors. You can get some compile errors by making sure you try to compile to a
"proper" post-ES2015 module format (--module esm or --module es2015 or
--module esnext even). There's also tslint rules such as "no-import-requires".

------
vallode
Issues like these are the reason we can thrive as a community. Not playing a
blame-game and offering direct feedback to a wonderful open source project.
Trying to get my own company to get more involved in feedback to the open
source tools we use as I think it is so extremely respectful and encouraging!

------
bsaul
A little off-topic:

I'm currently trying to find a way to write (type safe) business logic once,
then reuse it pretty much everywhere (mobile / web / desktop),and it seems to
me that typescript has become the only option. Javascript runtime is present
everywhere, and can interface with anything.

Does someone knows of another alternative (viable right now, or in the coming
months) ? I know llvm can theoretically target any platform, including wasm,
but how painfull is it in practice ? Can you write a line of code that does a
network request then expect it to run as it is on the browser and on mobile
platforms ?

~~~
GiorgioG
C# (with .NET Core 3.0 officially launching on the 23rd of September, Blazor
(WASM) support is included.) I have been playing around with Blazor a bit with
the preview releases and I have to say it's pretty slick.

~~~
stefano
Did they improve the download size?

~~~
cutler
No and on that basis it's unusable. However that's because it downloads the
whole runtime. Eventually MS intend to load only your compiled code.

~~~
mattferderer
You can avoid that issue with the Server Side Blazor concept for business side
portions of your application.

Depending on the application you can get extremely far with just using HTTP
requests to an API for anything business logic related. A lot of apps over
complicate themselves by trying to force a JS framework on the front end with
no benefit to the user. I seem to prefer using non-SPAs these days over SPAs
because very few places do them well. Google being a terrible SPA developer.

\- Edit - I forgot to mention Elixir's Phoenix has LiveView which is similar
to Server Side Blazor.

~~~
cutler
As I understood Blazor as compiled business logic rather than a bundled
runtime is due in a future release of .Net Core. Not sure if it's ready for
3.0 later this month but that will be the game-changer Blazor promises to be.
Yes, Phoenix Live View is similar but unfortunately Elixir hasn't really
captured sufficient mindshare for it to have an impact.

------
juliendc
I've recently built a Node server with TypeScript and it's a joy to use with
external libraries when the types are available. It's such a time saver to not
have to guess which method to call with which arguments (I've had only
experience with dynamic languages before). Some libraries don't have types or
they are outdated but it was a minority.

With the experience I've found that most of the type errors are actually
between the backend and the frontend in web applications. It's still hard to
fully type the entire flow from the database calls with the ORM to the objects
manipulation in the frontend.

How are you dealing with that? We used Nexus with GraphQL but it was still a
bit cumbersome.

~~~
gregplaysguitar
We use [https://graphql-code-generator.com/](https://graphql-code-
generator.com/) in combination with some scripts which update the graphql
schema at build time - works great. Using Apollo on the front end.

Having types straddle the client/server divide is a huge win

------
lxe
Almost every single Flow version upgrade is like this — every new version
brings a slew of errors due to Flow’s continuous movement away from
practicality towards “soundness”.

~~~
vicapow
I’m not sure I agree. I’ve found updating Flow in a rather large monorepo a
relatively straightforward process. The changes are usually rather small
because the team releases every two weeks. They also manage the update
internally within their company’s monorepo so they’ll usually find out about
these types of unexpected behavior changes before the community does.

That said, we do rely on automated error excludes (similar to eslint-ignore-
next-line) for things that cannot be fixed with codemods. Those errors were
always there it’s just now you know about them. Better to stem the bleeding by
updating the type checker to the latest version.

~~~
lxe
> I’ve found updating Flow in a rather large monorepo a relatively
> straightforward process.

I'm not sure you're being entirely honest here, as we both know through
feedback that flow updates in the said monorepo are one of the most burdensome
processes for its contributors.

As a maintainer/owner of a monorepo experience, it's crucial to maintain
empathy and honesty of how processes such as dependency and tooling updates
affect (and are perceived by) its users.

~~~
vicapow
Apologies, lxe. I can tell you're frustrated. If the the Flow upgrade process
has been burdensome to others, I'd really like to know. I've always thought
we've done a good job of updating internally on the platform team and that it
hasn't been a concern for end users. We did run into some pain points in
changing configuration options around the unnecessary optional-chaining lint
rules but that wasn't related to updating the Flow version. I think you may be
conflating those.

------
dmix
> but any time someone saves a Selection into a member variable, they ended up
> writing down whatever type TS inferred at that time, e.g.

> mySel: d3.Selection<HTMLElement, {}, null, undefined>;

I'm curious how Google and others approach adopting Typescript gradually, as
I'm pretty new to it, I'm assuming it goes like: The programmer converts code
to Typescript and when they come across return types they copy the inferred
type and add it to the codebase directly wherever possible. I'm assuming just
as a matter of using (untyped) libraries you need to rely on the output of
Typescript in order to try have every return typed.

So the biggest problem seems to be how TS infers things changed meaning you
can't always trust what you copied as staying consistent, even if the source
library doesn't change itself. That's always something to keep in mind for
overhead.

~~~
gmoot
The explicit type is optional. It's not that it's required to copy out
whatever the compiler inferred, it's just that lots of people do it to be
explicit.

~~~
dmix
All types are optional in typescript. I was just curious about Google's
approach to it and if they had some standard practice of always copying the
inferred type to get a great amount of coverage.

~~~
lonelappde
Copying the inferred type is the same as copying your runtime outputs into
your tests. It's a statement that you believe the result is correct and it's
on you to make that judgement call. It's not a policy to blindly copy
everything, because that defeats the purpose of type checking and testing,
turning your tests into " verify that nothing changed", not "verify that the
system behaves as intended".

~~~
dmix
Thanks, thats typically how I approach it as well, it's good to hear it
written out. I try not to append copied types without fully understanding
their structure either. But sometimes you just need to just trust it to solve
a problem.

There's plenty of blog posts and docs on advanced types but I'm interested in
the day-to-day best practical approaches people are taking adopting it. I
should look around for some literature or talks on the subject...

------
andreigaspar
So happy to see the attention Typescript is getting lately. Absolutely love
the language, and have been using it since it got released.

~~~
umvi
I tried using it with Angular, but it doesn't seem to help much. For example,
if you have something like:

    
    
        <button (click)="login(email, pass)" />
    

And then a TypeScript function like:

    
    
        login(email: string, pass: string) {
        
        }
    

TypeScript can't help you at all here because all the typing is determined at
runtime by Angular. Even if `email` is a number or a boolean, no problem, it
will just happily pass it in.

What benefit, then, does TypeScript provide? I understand it's compile-time
guarantees, but how does that help if the types are coming in from HTML land
which the TypeScript compiler doesn't examine at all?

~~~
threecreepio
For your template code in angular, there won't be any significant benefit to
using typescript. It could be worse than not having typescript at all, since
you can add type annotations to your 'login'-function that don't match up with
reality.

That's not really a typescript issue though, and it works great with libraries
that don't use string templates. From what I've seen the angular community
hasn't really prioritized a typecheck-able templating language.

~~~
dmix
Nor does Vue. The focus is on typing the reactive data that feeds the
templates which should be sufficient.

Plenty of things like HTML form elements take numbers or strings just fine, as
it all outputs to strings in the end. Additionally, by breaking up stuff into
smaller composable components there should be enough gating and typing layers,
at least with Vue/Vuex that's the case. If it was just plopping straight into
the elements 1-to-1 that might be a different story.

------
tannhaeuser
Is there a way to migrate jsdoc-annotated JavaScript code over to TS, and is
TS's minifier as good as Google's closure-compiler yet?

~~~
vbitz
Visual Studio Code has a "Code Fix" that can automatically copies JSDoc into
TypeScript annotations. This can be done to an entire file at once.

TypeScript does not come with a minifier. The code it produces is compatible
with the target version of JavaScript (e.g. compile ES6 modules into CommonJS)
but unminified.

~~~
tannhaeuser
I see. closure-compiler's "advanced" mode takes advantage of jsdoc type info
for minifying so I guess generic syntactical minification won't compress as
much for the time being.

~~~
evmar
We (Google TS team) maintain a tool[1] that transforms TS types into jsdoc
types for the purpose of feeding them into 'advanced' mode. We also (to answer
the grandparent question) maintain a tool[2] that converts Closure-annotated
TS into JS. (Why both ways? We transition JS->TS and check it in as the code
the user works with, while we use the TS->JS one within the compiler at
optimization time.)

[1] [https://github.com/angular/tsickle](https://github.com/angular/tsickle)

[2] 'gents', in this repo
[https://github.com/angular/clutz](https://github.com/angular/clutz)

------
msoad
filter(Boolean) is a bad idea anyways. I was bitten by this before.

~~~
y2bd
Why do you say so?

~~~
papln
One reason:

This isn't TS-specific; it's common in Python too: coercing to Bool has
language semantics (for whatever language you are in) which often don't match
the application semantics of your program. Application programmers don't (and
shouldn't have to, but for the language's over-eager coercions) always think
about the boolean semantics of all their objects. In particular, None and
empty/zero object are both False in Python, and Python style/linters push you
to avoid explicit comparison to None, which gets weird when your application
wants to treat empty objects as True because they have differen semantics from
None. (For example, in a security function, None may mean no-op / fallback to
default, but Empty might mean "Reject all".

~~~
WorldMaker
Another reason that is TS-specific is that of JS functions can be highly
variadic in the number of arguments and a lot of subtle runtime bugs can be
found in blindly passing arguments without checking their count. If filter
changes from returning only one thing to say two (for instance, an index
count), Boolean may produce a runtime error for having too many arguments, may
silently ignore extra arguments, may interpret an extra argument as changing
the behavior, or some combination of all three depending on strictness versus
compatibility level, executing browser, phase of the moon, etc.

Boolean itself I've not had trouble with, but things like map(parseInt) is the
big one that bites a lot of junior developers all the time in TS/JS. (parseInt
takes an optional second argument for radix, so in cases where the second
argument returned by map is an index count, which is likely, you get it
parsing in base-0, base-1, base-2, … which is almost never something you'd do
intentionally.)

------
_the_inflator
It is very funny to read this from Google, since we talked about similar
problems with Google regarding Angular upgrades, where features were modified
that Google considered not used/no use cases known, while we were relying on
these.

From my point of view Core members of any super large project (like React,
Angular, TypeScript) are limited by design in what they perceive as their
target audience and their use cases. This is simply a matter of fact: even as
a core dev you cannot know how every dev uses your product.

So this is some sort of left-pad moment for TypeScript.

------
psv1
Not a TypeScript user, but what really stood out to me is that Google are
using a monorepo.

~~~
apalmer
that stood out to me too... not the monorepo really, but the fact its a
monorepo of a billion lines of code. seems almost impossible to maintain when
you have a dependency change at the lowest levels that affects numerous
projects.If i am understanding google was in a situation where they had to
update every project that used typescript inside the entire company at the
same time, that seems untenable.

~~~
yoz-y
Mostly it means that you have to be really thoughtful when introducing a
breaking change to a low level library.

~~~
papln
Or you can YOLO / Leroy Jenkins the change and let everyone else fix the
breakage you create, or see if they demand a rollback.

~~~
yoz-y
They will not only demand a rollback but also do it. Besides, low level
changes at this scale require a copious amount of approvals.

------
brlewis
I'm really curious how many lines of TypeScript are in use at Google. I bet
most JS is still Closure.

------
jbverschoor
Google sure has a lot of comments these days

