Leading up to TypeScript 3.0, we actively sought out users across different companies to discuss their biggest pain-points with TypeScript. One common theme truly stood out: error messages and UX.
It turns out that at these organizations, TypeScript is often the first typed language that their engineers encounter. In some cases, JavaScript is the only language many of their engineers know. This took us by surprise, since most programmers we speak to about JavaScript tend to at least know one other language such as English or Latvian. Nevertheless, we took this seriously, and strived to do better here. For a couple of versions, we even put together meta-issues to improve the UX.
The reception of improvements from 2.9 to 3.2 was phenomenal. Despite being such a basic need, TypeScript became significantly more approachable thanks to this focus.
But we're not close to done.
We can do better. That's why today, I'm proposing a revolutionary new strategy for error messages everywhere.
There's an old saying: it's not what you say, it's how you say it. Now, I've gotten in trouble a few dozen times thanks to that saying, but I have a feeling that it might go over better when applied to a type-checker for optional static types like TypeScript. Telling people about errors differently goes a long way.
Inspired by TypeScript's --pretty
flag, I'm proposing a new diagnostic reporting mode.
--noExplicitErrors
flagToday, JavaScript users experience no type-checking errors before running their code. Instead, they deal with much cleaner runtime errors like
Uncaught TypeError: undefined is not a function
at <anonymous>:1017:19
at ezekiel:25:17
at <anonymous>:1017:19
at cage:4:33
What's nice about these error messages?
<anonymous>:1017:19
).Let's contrast that with TypeScript. Consider the following code:
let myPromise = Promise.resolve([
Math.random() ? { success: true, result: [1, 2, 3, "hello"]} :
{ success: false, error: "hello" }
] as const);
function foo(param: PromiseLike<{ success: true, error: string }>) {
// ...
}
foo(myPromise);
This appears to be every-day very extremely good readable good code, so one would hope for little trouble in compiling/running it. What's TypeScript have to say about it?
Argument of type 'Promise<readonly [{ success: boolean; result: (string | number)[]; error?: undefined; } | { success: boolean; error: string; result?: undefined; }]>' is not assignable to parameter of type 'PromiseLike<{ success: true; error: string; }>'.
Types of property 'then' are incompatible.
Type '<TResult1 = readonly [{ success: boolean; result: (string | number)[]; error?: undefined; } | { success: boolean; error: string; result?: undefined; }], TResult2 = never>(onfulfilled?: (value: readonly [{ success: boolean; result: (string | number)[]; error?: undefined; } | { ...; }]) => TResult1 | PromiseLike<...>,...' is not assignable to type '<TResult1 = { success: true; error: string; }, TResult2 = never>(onfulfilled?: (value: { success: true; error: string; }) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => PromiseLike<...>'.
Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
Types of parameters 'value' and 'value' are incompatible.
Type 'readonly [{ success: boolean; result: (string | number)[]; error?: undefined; } | { success: boolean; error: string; result?: undefined; }]' is missing the following properties from type '{ success: true; error: string; }': success, error
O͠H M̵Y͝ ̸G͢O͜D ̴ͪ́̃̌ͬ̿M̸̋̅͆ͪͭ̄̀̒͠Ȃ̅ͨ̿͑ͤͯͫK̈́̓ͩͪȆ̽ͨ̾ ̷ͯ͌́ͩͬ̚Iͮ͋͗ͩ͂҉͡Tͪ̄̾ͬͮ͆ ̴ͯ̏́̓̈́͠͠Sͦ͗́͘T͐ͤ̆̉̚҉͘͝Ó̢ͭ͘͞P- uh, I mean, look, TypeScript has saved us!
The newly proposed --noExplicitErrors
flag entirely eliminates the problem of long, difficult-to-parse error messages. We took a look at every single error message we provided, and thought long and hard about whether each message was fully applicable to every user and would unambiguously improve their lives. If a single person might not benefit from the message, it likely wasn't worth its weight, and was entirely omitted under --noExplicitErrors
.
So what's an error message for this snippet look like in --noExplicitErrors
? Well, let's find out. First we need to run the compiler
tsc --strict --noExplicitErrors sample.ts
That's it! Now let's take a look at those beautiful new error messages!
It's gorgeous 😍. --noExplicitErrors
has removed every single confusing part from that original error message.
This flag will truly help TypeScript meet the JavaScript community where it is - an error-checking experience without any errors! Existing TypeScript users - just imagine how much less anxious you'll be the next time you hover over a red squiggle in your editor!
While the feature isn't quite ready, we'll be looking for feedback and beta-testers over the next few months. The TypeScript team has tried running with this new flag recently, and while we've had a harder time in some cases of figuring out what was going wrong, we felt less like we were "fighting" the type-checker and more like we were just writing code. The type-checker has become our friend, and real friends will never bring up your problems.
All of our compiler option names are very carefully thought out, and we believe it shows. Here's a few examples of well-named compiler options:
isolatedModules
noImplicitThis
alwaysStrict
noImplicitUseStrict
types
typeRoots
rootDir
rootDirs
baseUrl
allowSyntheticDefaultImports
What's so good about these compiler flag names? They communicate exactly what they do.
Which is how we arrived at the current name of this new mode. --noExplicitErrors
is going to be the mode for unambiguously clean errors.
If the error messages are still hard to read under --noExplicitErrors
, consider filing an issue on our issue tracker, or try re-reading the error message a few times.
Last year, we received a wave of interest in haiku-formatted error messages. While it's been in the "Future" bucket on our rolling feature roadmap, it unfortunately will have to take a back seat to --noExplicitErrors
. We think that --noExplicitErrors
will enable the next big wave of TypeScript users, and we simply can't wait on that.
Besides, we'd probably do limericks first anyway. A haiku doesn't even rhyme.
Might I also suggest noExplicitTypes
for users that love typescript but think types are a pain to read and write. In this mode everything is typed as any
and "just builds".
@dragomirtitian can you please keep your suggestions less good than mine? I am trying to get promoted with this initiative.
Can you add a feature to override this on a per file basis?
@spion Let's please keep the discussion productive and limit ourselves to reasonable requests.
It's a great idea. I would not have expected anything better on this beautiful day. I would implement it exactly in one year.
I'm not sure if this is in conflict with noImplicitAny
? Logic dictates its equivalent to explicitEvery
, which subsumes explicitErrors
and this feature is noExplicitErrors
In some cases, JavaScript is the only language many of their engineers know. This took us by surprise, since most programmers we speak to about JavaScript tend to at least know one other language such as English or Latvian.
Heh. :-P
Given that 40% of type errors are actually compiler bugs I hope the language doesn't suffer because of this. Is there anyway --noExplicitErrors
can be setup to just email our error messages to the TypeScript team directly?
Can you also support the opposite flag, like --verboseFlexboxErrors
as I want the output of each error elegantly takes a full 14" screen so I could then just page-down / page-up through them in a full screen mode?
TypeScript community needs more than that. Here's my proposal.
JavaScript users who start to use TypeScript - either by migrating an existing codebase or starting a new project - experience painful moments, when the compiler throws errors. That's not what they got used to in JavaScript. Criticizing someone else's code and pointing out all potential mistakes is unkind and, if used in a long run, can be demotivating and destructive. Also, it might lead to scrum retrospective meetings crossing the timebox regularly, for people to clear their minds.
It's a challenging issue how to prevent programmers, who code in statically typed languages, from seeing huge amounts of errors every day. Think about all the suffering. After a long research, there is a solution though.
--noErrorsOnCompile
flagIn order to satisfy the demand for a more pleasant programming experience with TypeScript, this proposal introduces a compiler flag that doesn't emit errors while compiling. This way we can get the best of both worlds - make use of types (TypeScript) and have no compilation errors (JavaScript).
before:
after:
see? no errors on compile!
Edit: As community members, pointed out, using both --noEmitOnErrors
along with --noErrorsOnCompile
make your TypeScript code always emit output. How cool is that.
@dragomirtitian @ducin I think there's so much great cross-pollination here. It's important to ask exactly what the overlap is between noErrorsOnCompile
and noExplicitTypes
. I think the two can potentially coexist even if they do mostly the same thing, especially given that we want to broaden our command-line expressiveness.
@DanielRosenwasser Honestly, I don't like this. It's too opinionated -- I don't _want_ an opinionated compiler, frankly.
Isn't there some reasonable middle? Like why does it have to be all or nothing? Couldn't we _just_ do something like --onlyReasonableAmountsOfErrors
or something? Or --noExplicitErrorsAfter3Pm
?
Actually, what I really want is a --friendly
flag
Uncaught TypeError: undefined is not a function
at <anonymous>:1017:19
at ezekiel:25:17
at <anonymous>:1017:19
at cage:4:33
I see what you did there 😄:
ezekiel:25:17
https://youtu.be/x2WK_eWihdU?t=56
cage:4:33
https://www.google.com/search?r&q=cage+4%2733"
<anonymous>:1017:19
https://www.youtube.com/watch?v=dQw4w9WgXcQ
Most helpful comment
Might I also suggest
noExplicitTypes
for users that love typescript but think types are a pain to read and write. In this mode everything is typed asany
and "just builds".