tsc-silent. A TypeScript compiler with --suppress flag
tsc-silent is a tiny tool, which helps a lot. Similarly to -nowarn
in C#, it supports ‑‑suppress
or ‑‑suppressConfig
flags and replaces tsc
when you wish to ignore certain errors in the compiler’s output. The most useful feature is the ability to ignore specific errors coming from a certain file path or file pattern.
Okay, Compiler…
With TypeScript, I often spend time fixing problems that are revealed after turning another compiler’s flag on.
When I do so, my routine is:
- turn the compiler option on (say,
--strictNullChecks
in many-years-old-legacy-code) - fix TS problems
profitcommit
However, it’s quite often that the list of errors is longer than my “can fix in a day” capability. And sometimes it takes weeks to make all the necessary changes before I can finally turn on the flag in the main branch. So, this “fix TS problems” step is repeated day after day.
Of course, while you work on these errors, there is a good chance that other developers’ code, committed to the project, brings new errors because the flag is still off in the main branch. And you have to fix these too.
With tsc-silent
in place, I first turn the compiler option on (ignoring errors coming from some directories) in the main branch and my daily routine changes to:
- fix a unit of “can fix in a day” problems, and exclude fixed files from the list of ignored paths
- commit and profit every day
Ignore responsibly
Even though tsc-silent
helps a lot, it can be risky if you aren’t careful enough.
Error codes that are too vague
Say, for good reasons, you try to turn on --strictFunctionTypes
. It will report some new errors. For example, as in the case from TypeScript@2.6 announce:
It fails with a very generic error #2322. There are many other problems reported under this code, and they will all be silently ignored. If it is the case that you turn off a similar umbrella error somewhere, you may end up in more trouble than you were before you decided to introduce --strictFunctionTypes
.
Error codes change
As @RyanCavanaugh mentioned, error codes change. With every release TypeScript brings new or more specific errors, so you have to manually control TypeScript upgrades to avoid problems.
Is the flag to become standard?
There is a suggestion to ignore errors in the compiler’s output by id, where valid concerns were raised by the TypeScript team, and I concur with them.
Except for the practical reasons mentioned above, I don’t agree, that the compiler should allow private members’ access in test files. From a pragmatic point of view, I cannot trust something that forgives my errors based on whether or not the file name matches a regexp hidden in the project config. They are still errors, and I want to see these errors in IDE myself; I want to be sure that everyone sees them because I wish for these errors to get fixed.
I doubt that the flag will ever become a part of the compiler. Luckily, TypeScript is a great extendable tool, and when extended and used with caution it can even handle such non-trivial use cases.
Type aliases to `any`
any
solves any problem
As you know, any
considered harmful in TypeScript. I do believe it’s better to avoid it, and especially after unknown
was introduced to replace any
in lots of cases. To discourage developers from using it, I can even imagine no-any
rule turned on as a warning, but I don’t think deprecating any
should be a goal.
Having a number of dependencies, it might be problematic to integrate all the third-party code without using any
. Sometimes, when you upgrade a single package, some of its’ types rely on other package’s types, so you have to add a sprinkle of any
. Or, pretty commonly, every TypeScript upgrade brings a handful of new errors across whole codebase. And if you don’t have a full picture or even authority to make substantial changes in someone else’s code, at times you have to suppress these errors with another splash of any
. Thus, maintaining zero-any codebase seems to me excessively and unduly time consuming.
But we still want to discourage people from using it, and that’s where type aliases to any
became handy:
/** */
type temporaryAny = any;
temporaryAny
solves refactoring problems mentioned above by not using any
and simplifying spotting refactoring-only any
.
Once you see the benefits, you realise there is nothing that stops you from annotating
every non-semantic any
with temporaryAnyUntillCertainProblemSolved
:
/**
* Type alias for string types that have to be narrowed to literal types
*/
type temporaryString = string;
/**
* Type alias for number types that have to be narrowed to literal types
* https://github.com/Microsoft/TypeScript/issues/15480
*/
type temporaryNumber = number;
/**
* Used for situations when conditional types (for example `ReturnValue<>`) should be used.
*/
type temporaryAnyUntilTypescript2_8 = temporaryAny;
And even seemingly too verbose:
/**
* For generic type `T`:
* ```
* const bar: T["bar"] // string | null | undefined
* if (bar) {
* bar // is still `string | null | undefined`
* }
* ```
* https://github.com/Microsoft/TypeScript/issues/22214
*/
type temporaryAnyWhileTypeGuardsNotWorkingForIndexedTypesWithGenerics = temporaryAny;
Every time I type any
I wonder if it’s an any or temporary any.
Note on TypeScript's exhaustive type checks in scope of Redux's reducer
After introducing discriminated union types, they showed a way to get closer to pattern matching by havin exhaustiveness checks in default branch of a switch-case
statement.
In documentation there is an implementation of an assertion function, that returns never
both runtime- and type-wise.
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
Intuitively, similar approach could be used in Redux’ reducer
function to make sure each action has been handled:
type Action = TurnOnAction | TurnOffAction;
function reducer(state: State, action: Action) {
switch (action.type) {
case TURN_ON:
return ...
case TURN_OFF:
return ...
default:
assertNever(action);
...
The check seems fair type-wise (action
indeed should have type never
).
Apparently, we don’t exhaustively check all the possible actions, instead we specify a subset of actions we handle in the particular reducer (type Action = ...
).
We don’t care about the other actions (such as internal @@redux/INIT), they just fall through to default case.
But that way we have a runtime problem, because assertNever
throws on every call, so what do we do?
Right, we add a second parameter specifying whether we throw or not:
function assertNever(value: never, noThrow?: boolean): never {
if (noThrow) {
return value
}
throw new Error(...);
}
Fair enough, but, we are about to make a mistake. See, with noThrow === true
signature still says the function returns never
, while it does return a value.
What important about exhaustive checks, is that in most cases assertNever
should be prepended with return
statement so that compiler inters correct return type of a function. This habit and the fact that never
is being omitted from union types, may lead to
function reducer(...): State
switch (action.type) {
case TURN_ON:
return ... // State
case TURN_OFF:
return ... // State
default:
return assertNever(action, true); // never
}
}
And voilà, return type of a function is State | State | never
simplified to State
and we have happy compiler and a runtime error.
Advise if simple: don’t pretend you return never
in the case you return anything. There is unknown
type, which absorbs every type in union, and turned to be a pretty good replacement for never
in this case. That’s how assertNever
could be implemented:
function assertNever(value: never): never;
function assertNever(value: never, noThrow: true): unknown;
function assertNever(value: never, noThrow?: true): never | unknown {
if (!noThrow) {
throw new Error("...");
}
return undefined as never;
}
Or, alternatively, you can separate implementations:
/** Non throwing one */
function assertNever(value: never): unknown {...}
/** The one that throws */
function assertNeverEver(value: never): never {...}
Updated, thanks to @fljot.
It was a mistake to even think about a non-throwing assertNever
. If the non-throwing one is being used only in reducer’s default
branch, then so be it. Let’s just endReducer
:
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
function endReducer<T>(state: T, action: never): T {
return state;
}
The simpler the better:
function reducer(state: State, action: Actions)...
switch (action.type) {
case ...
default:
return endReducer(state, action);
}
}
Updated, thanks to u/AngularBeginner .
Other option is to have throwing and non-throwing functions with the same signature:
function assertNever(x: never): never { throw new Error(`Unxpected value: ${x}`); }
function ensureNever(x: never): void { /* Intentionally empty */ }
Stay safe ♱
Prettier makes your code shittier
As the whole idea of Prettier can fit in a tweet, my response could probably fit in a simple «I don’t like the way you solve a possibly nonexistent problem» too.
But since they become so popular, I decided to take apart a paragraph from Prettier’s homepage
By far the biggest reason for adopting Prettier is to stop all the on-going debates over styles. It is generally accepted that having a common style guide is valuable for a project and team but getting there is a very painful and unrewarding process
Well…
By far the biggest reason for adopting Prettier is to stop all the on-going debates over styles
That does sound reasonable, but we never fight for the right place for a new line. We do fight for optimal line-length and function
vs =>
, but Prettier doesn’t help you to solve any of these problems
having a common style guide is valuable for a project and team
Amen!
but getting there is a very painful and unrewarding process
It is, indeed! But let’s take a look at object shorthands. They look better and even easier to read. However, I do not follow this rule because TypeScript refactoring tool doesn’t work in that case. And I value the ability to rename object’s members way more than concise syntax. Same goes to default import/export, which we avoid. Thus, if you care about maintainability, you still have to have tslint
, most likely with custom rules developed within your company.
It is painful to get there, but it’s not very painful for sure. And that is a really rewarding process, which pays you back immediately.
To me, Prettier does help you in places where you don’t care about code style and does not allow you to have a custom format in places where it matters, sometimes it reduces readability:
export const selectIsImmersiveLayout: StoreSelector<boolean> = () => false;
// prettier ↓
export const selectIsImmersiveLayout: StoreSelector<
boolean
> = () => false;
sometimes, it propagates inconsistency and causes git conflicts by changing more than you intended to:
function foo(does, fit)
// single rename results in several lines changed ↓
function foo(
doesnt,
fit,
)
prevents you from manually formatting your code where it matters, say, complicated logic conditions:
if (
A
&& B
&& (
C
||
D
)
) {
// prettier ↓
if (A &&
B &&
(C || D)
// or whatever mathes configured line length,
// but not the format you put there
so why?
How to be an outstanding Javascript developer
There are so many exciting things happening in Javascript world. It’s so great to float in the ocean full of new frameworks, isomorphic apps and higher order components. It’s pure joy. However, being outstanding developer isn’t easy.
Here is the thing, you are either shark or sheep in the community. Nowadays, you have to be a shark, by all means, there is no time to look around or back.
Follow the trends and be open
I hope that comes without saying. Follow “Javascript Weekly”. Star new projects on GitHub. Retweet as many announces as you can.
It’s okay if you don’t have time to dig into a new framework, no one has. At least you help the community to stay aware and improve by retweeting and starring.
Know your side
It’s a war.
There are sharks, on one side of the barricade, who try to keep up and be on the crest of a wave. These guys are always ready to introduce new awesome framework and make the world a better place.
There are sheep on the other side. These naggers always concerned, busy refactoring something and hardly know how awesome react-create-app
is.
Do you wanna be a retrograde or innovative? There it is.
Be functional
Functional programming is a must today. The good thing is it won’t take much of your time. You have to know what is a pure function (it’s good), side-effects (they are bad) and remember that there are no side-effects in functional code. Ah, also, there is a mystical “monad” who no one understands. Keep that in mind and don’t push too hard trying to wrap your head around it.
Love Javascript
In order to be a good Javascript developer, you have to love Javascript.
Here it is, our philosophy is basically this, and this is something that we live by, and we always have, and we always will. Don't ever, for any reason, ever, no matter what, no matter where, or who, or who you are with, or where you are going, or where you've been, ever, for any reason whatsoever, think that there is something better than Javascript.
Be opinionated
Your opinion is another thing that makes you delightful companion. See, you love javascript, follow the trends, you are smart and registered on Twitter. So there is no way you don’t have an opinion on a particular thing.
— How do you compare Angular and React? Don’t they solve different problems?
— Duh, they are both frameworks. And React has three times more stars on GitHub, and it beats stupid Angular in ToDoMVC! React is way better than Angular!
— What do you think of types in Javascript?
— Javascript is great, and you don’t need types!
— Well, it still really want to try them, just not sure how TypeScript is different from Flow…
— Of course, Flow is better, because they know the shit at Facebook!
– Hey, what do you think about NPM and Yarn?
— Of course, Yarn is better, because they know the shit at Facebook!
You can even allow yourself to be a bit of an asshole. Smart people are usually like that.
– Dude, I feel like I struggle a lot with server requests in Redux…
— That’s because you don’t know shit about functional programming, idiot! It’s called “side-effect”.
See the strategy? Again, to be a good companion you have to remember many things as GitHub’s star ratio, latest comparison’s results and frameworks’ names after all.
Good luck
These simple advice will make you an outstanding Javascript developer in no time. You are smart enough, just follow them blindly.
Keep up! Star! Retweet!