DAY 19 OF 30
Type assertions and satisfies
Yesterday, in one sentence
never type in TypeScript?The type for a value that can never exist. TypeScript produces it when a type is impossible, such as an intersection of two types nothing can satisfy, or when narrowing has ruled out every member of a union.
Did you say:
- ✓a value that can never exist
- ✓produced by an impossible type
- ✓produced when narrowing rules out every member of a union
Interview question
Add an exhaustiveness check. After handling every case, assign the value to a
variable typed never. It only compiles while everything has been ruled out, so
a new object type breaks the build.
Did you say:
- ✓add an exhaustiveness check
- ✓assign the value to a variable typed `never` after the last branch
- ✓a new object type stops it compiling
A type assertion tells TypeScript to treat a value as a particular type. You write
it with the as keyword.
const power = value as string;
A type assertion does not convert anything and it does not check anything. It only changes the type TypeScript uses for that value.
You can use a type assertion when TypeScript's type for a value does not match what
you know the value to be. This usually happens with values coming back from code you
did not write. For example, JSON.parse returns any, because it is defined outside
your code and has no way to know what is in the string you passed it. You might know
that this particular string will parse into an object with the type
{ name: string }.
const stored = JSON.parse(raw) as { name: string };
stored.name.toUpperCase();
// allowed, because you asserted it was the object type { name: string }
If the stored data does not match, the TypeScript compiler will not catch it, and the error appears at runtimeRuntime instead.
TypeScript will refuse an assertion between two types with nothing in common.
const rank = 41 as string;
// ❌ Conversion of type 'number' to type 'string' may be a mistake
satisfies
satisfies checks a value against a type without changing it. Where as overrides
the type and checks nothing, satisfies checks and keeps what was inferred.
The problem it solves shows up when the declared type is wider than the value you
wrote. For example, the type declares string | number, and later you want to use a
number method on it.
type Hero = {
level: string | number;
};
const annotated: Hero = {
level: 9,
};
// level: string | number
annotated.level.toFixed();
// ❌ Property 'toFixed' does not exist on type 'string | number'
You wrote a number, but the annotation replaced that with string | number, so
TypeScript will not let you use it as a number. Removing the annotation fixes that, but
then nothing checks the object against Hero at all.
Putting satisfies Hero after the object checks it against Hero, then leaves the
inferred type in place. So level stays a number rather than becoming
string | number.
type Hero = {
level: string | number;
};
const hero = {
level: 9,
} satisfies Hero;
// level: number
hero.level.toFixed(); // ok, level is a number
The value is still checked, so a bad one fails.
const broken = {
level: true,
} satisfies Hero;
// ❌ Type 'boolean' is not assignable to type 'string | number'
Three ways to write the same object, and what each one gives you:
- type annotation checks the value, but widens it to the declared type
- type inference keeps the inferred type, but checks nothing
satisfieschecks the value and keeps the inferred type
Check yourself
hero.level here?
type Hero = {
level: string | number
}
const hero = {
level: 9
} satisfies Hero
satisfies checks the object against Hero but does not replace the type that was inferred, so level stays a number. An annotation would have widened it to string | number. Getting the literal type 9 would need as const as well.
Telling TypeScript to treat a value as a particular type, written with as. It
changes the type without checking or converting anything. You use one when
TypeScript's type for a value does not match what you know the value to be.
Did you say:
- ✓tells TypeScript to treat a value as a particular type
- ✓written with `as`
- ✓checks nothing and converts nothing
- ✓used when TypeScript's type does not match what you know the value to be