Does anyone have an idea why Typescript is not complaining about the missing c
?
type MyType = {a: number, b: number, c: number}
const Defaults = {myDefault: {a: 5, b: 6} as MyType}
If I have
const Defaults = {myDefault: {a: 5, b: 6, d: 9} as MyType}
it's complaining that c
is a missing property (not complaining d is unknown). But it's not complaining if I have just ab
or abcd
.
Adding required
also changes nothing.
const Defaults = {myDefault: {a: 5, b: 6} as Required<MyType>}
Why could this be?
CodePudding user response:
The type assertion in TypeScript allows you to
convert to a more specific or less specific version of a type
If type MyType = {a: number, b: number, c: number}
and const Defaults = {myDefault: {a: 5, b: 6} as MyType}
, then for TypeScript {myDefault: {a: 5, b: 6}}
sufficiently overlapps with MyType
. Its type is contained in MyType
.
Now, consider Defaults = {myDefault: {a: 5, b: 6, c:4, d: 3} as MyType}
, then this is also allowed. The object {myDefault: {a: 5, b: 6, c:4, d: 3}}
is more specific than MyType
and we convert it to its less specific version, namely a type that only contains the properties a, b
and c
.
The type of MyType
is contained in the type of the object {myDefault: {a: 5, b: 6, c:4, d: 3}}
.
For TypeScript the poperty d:3
does not exist for Defaults
since we have asserted MyType
. Thus writing
Defaults.myDefault.d
yields in
Property 'd' does not exist on type 'MyType'.
Of course we could access it at runtime since
type assertions are removed at compile-time, there is no runtime checking associated with a type assertion
On the other hand, when writing
const Defaults = {myDefault: {a: 5, b: 6, d: 9} as MyType}
the type of the object {{myDefault: {a: 5, b: 6, d: 9}}
is neither contained in MyType
nor is MyType
contained in the type of {{myDefault: {a: 5, b: 6, d: 9}}
.
This correctly yields to the error message
Conversion of type '{ a: number; b: number; d: number; }' to type 'MyType' may be a mistake because neither type sufficiently overlaps with the other.
But, we notice the following. The intersection of MyType
and the type of { a: number; b: number; d: number; }
is {a: number, b: number}
.
Writing
const Defaults = {myDefault: {a: 5, b: 6,d:4} as MyType | {a:number, b:number}}
works. However, the property c
can of course not be accessed.
Edit: In response to the comments. The Required
construct in TypeScript does what you think it does, but with a minor difference. It turns all optional properties to required, which makes it the opposite of Partial
. This has nothing to do with the type we are assigning to a given object, but whether or not the properties of that type are required or optional.
Consider the following.
type MyType = {a: number, b: number, c?: number}
const Defaults = {myDefault: {a: 5, b:2} as MyType}
Defaults.myDefault.c
Checking the access to c
yields
(property) c?: number | undefined
However, asserting the type MyType
as required
type MyType = {a: number, b: number, c?: number}
const Defaults = {myDefault: {a: 5, b:2} as Required<MyType>}
Defaults.myDefault.c
yields
(property) c: number
CodePudding user response:
David Scholz answer seems right, but I think you may prefer to not use the as
operator in this case. If you fully type the variable Defaults
, TypeScript will complain c
is not specified as expected:
type MyType = {a: number, b: number, c: number}
type DefaultsType = {myDefault: MyType};
const defaults: DefaultsType = {myDefault: {a: 5, b: 6}}