17
Do you really know TypeScript? (2): Being strict
The TypeScript transpiler has an overwhelming set of options 🥵, but don't worry, you don't have to know them all.
Though, you should know these two really well:
noImplicitAny
strictNullChecks
When noImplicitAny
is enabled all variables must have a known type.
function greet(name) {
return `Hello, ${name}`
}
If you hover over the function (and your IDE has TypeScript capabilities) you will see that it infers that name
is of type any
.
We can say that name
is implicitly of type any
, and if noImplicitAny
is disabled TypeScript will rightfully complain 🙂
As we'll see more in depth later, any
bypasses TypeScript's type checks, making values of the any
type assignable to anything.
Using the any
type should generally be your last resort and if you really need to use it, you have to do so explicitly if noImplicitAny
is enabled.
Although noImplicitAny
enables you to make the most out of TypeScript, it can be tough to have this setting enabled if you are migrating your codebase from JavaScript, for example.
As we already mentioned, you can see types in TypeScript as sets of values.
strictNullChecks
controls if null
and undefined
are part of every type.
const jame: Person = null
// It'll throw "cannot read 'greet' of undefined" at runtime
jame.greet()
This code is going to throw an error when you execute it.
But, with strictNullChecks
enabled, TypeScript will tell you at compile time instead:
Type 'null' is not assignable to type 'Person'.
There are more "strict" settings that modulate how picky TypeScript is and you can turn them all on with strict: true
.
I would advise you to do so, specially if you are starting a project from scratch.
Before introducing the empty and universal sets as promised, we have to talk about any
, which is often perceived as the universal set.
What should I use any
for, then?
TypeScript is a gradual type system, you can type some parts of your code and leave others untyped. any
enables that, disabling the type checks.
- You can assign a value of the
any
type to anything - You can assign anything to a variable of the
any
type
any
doesn't fit in the "type as a set of values" model, since a set cannot be a subset and a superset of everything at the same time.
// No errors even with strict: true
const age: number = "4" as any
const name: any = 3.1416
Be specially cautious when it comes to using any
as a return type as it can spread to other well typed parts of your code that make use of said function.
Important points of unknown
:
- Any type is assignable to
unknown
because every type is a subset of it. - But
unknown
is not assignable to any type but itself (orany
) because it is not the subset of any other type. - Attempting to access a property on a value of the type
unknown
is an error.
The last point is key, specially when using it as an alternative to any
for edge cases when we really don't know the return type of a function, for example.
When using unknown
, the untyped code doesn't spread as we need to narrow the types in it in order to use it.
Besides narrowing it with an assertion, some libraries use generics for this:
function query<T>(q: string): T;
const result = db.query<User[]>('select * from user')
The never
type is the opposite of unknown
:
- Nothing is assignable to
never
because no set is a subset of the empty set. -
never
is assignable to everything, because the empty set is the subset of every set.
The use of never
is not as frequent as unknown
but it does have an use case that I like a lot called exhaustive type checking:
type SpanishChampionsWinners = 'Real Madrid' | 'Barcelona'
function getChampionsCount(team: SpanishChampionsWinners): number {
switch (team) {
case 'Real Madrid':
return 13;
case 'Barcelona':
return 5;
default:
const exhaustiveCheck: never = team;
throw new Error(`We forgot: ${team}`);
}
}
If one day "Atlético de Madrid" wins a Champions title, adding it to the SpanishChampionsWinners
type will make this code complain since no value is assignable to never
.
- Be as strict as possible with your TypeScript settings and know
noImplicitAny
andstrictNullChecks
well. - Understand that
any
does not fit in the "types as sets" model, being a mechanism to avoid types in parts of your code. - Try to isolate the untyped parts of your code and be aware of the
any
spreading. - Understand why
unknown
is preferable toany
when handling edge cases. - Get the idea of
never
and use it for exhaustive checking.
17