Even with basarat explanations on type assertions in his great book TypeScript Deep Dive:
Basically, the assertion from type
S
toT
succeeds if eitherS
is a subtype ofT
orT
is a subtype ofS
.
I was wondering what is the exact difference between type annotation variable: type
and type assertion expression as type
, more precisely when type assertion is working or not, especially regarding that TypeScript errors are sometime surprising: see them for the foo3
and foo5
variables bellow:
interface Foo {
n?: number;
s: string;
}
const foo1 = {} as Foo; // ok
const foo2 = { n: 1 } as Foo; // ok
const foo3 = { n: '' } as Foo; // KO: "Property 's' is missing..."
const foo4 = { n: '' as any } as Foo; // ok
const foo5 = { n: 1, x: 2 } as Foo; // KO: "Property 's' is missing..."
const foo6 = { s: '', x: 2 } as Foo; // ok
const foo7 = { s: 1, x: 2 } as Foo; // KO: "Types of property 's' are incompatible."
Other difference I've noticed: renaming the n
property in the Foo
interface in VSCode does not propagate to expressions being asserted, while it's working with type annotated variables.
I was wondering what is the exact difference between type annotation
variable: type
and type assertion expression as type
Type declaration variable: type
tells the compiler that the variable must always conform to the declared type. It is used by typechecker when a value is assigned to the variable (the value must be compatible with the declared type), and also whenever the variable is used (the declared type of the variable must be compatible with whatever way the variable is used in each particular place).
Type assertion overrides built-in type compatibility rules. It allows you to tell the compiler that you know that the value actually conforms to the type you give in the assertion, thus suppressing error message about type incompatibility. There are limits, however - you can't just assert that variable has any type you want (BTW there is any
type just for that). As you quoted in the question, for type assertion to work,
the assertion from type S to T succeeds if either S is a subtype of T or T is a subtype of S
It works exactly in that way in each example:
const foo3 = { n: '' } as Foo; // KO: "Property 's' is missing..."
Here two types: {n?: number, s: string}
and {n: string}
are checked for compatibility - if any of them can be converted to another. It can't be done either way: in one way, {n: string}
is missing non-optional s
and n
has the wrong type (must be number | undefined
); in another way, {n?: number, s: string}
has wrong type for n
(must be string
).
The complete error message is
Type '{ n: string; }' cannot be converted to type 'Foo'.
Property 's' is missing in type '{ n: string; }'.
When reporting structural type incompatibility, the compiler chooses just one incompatible property to show in the error message - it could be any of the three incompatibilities mentioned above.
const foo4 = { n: '' as any } as Foo; // ok
Works because {n?: number, s: string}
is compatible with {n: any}
: the first one can be assigned to the second - any
is compatible with anything, and s
is just ignored (basically, a value is compatible with the type if it has all non-optional properties compatible with declared type)
const foo5 = { n: 1, x: 2 } as Foo; // KO: "Property 's' is missing..."
{n: number, x: number}
is not assignable to {n?: number, s: string}
-s
is missing, as compiler says:
Type '{ n: number; x: number; }' cannot be converted to type 'Foo'.
Property 's' is missing in type '{ n: number; x: number; }'.
const foo6 = { s: '', x: 2 } as Foo; // ok
Works because {s: string, x: number}
is assignable to {n?: number, s: string}
: s
is OK, missing n
is OK because it's declared as optional, extra x
is ignored
const foo7 = { s: 1, x: 2 } as Foo; // KO: "Types of property 's' are incompatible."
Type of s
is incompatible:
Type '{ s: number; x: number; }' cannot be converted to type 'Foo'.
Types of property 's' are incompatible.
Type 'number' is not comparable to type 'string'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With