score:2

Accepted answer

Type assertions can only be used to convert between a type and a subtype of it.

Let's say you declared the following variables:

declare var foo: number | string;
declare var bar: number;

Note number is a subtype of number | string, meaning any value that matches the type number (e.g. 3) also matches number | string. Therefore, it is allowed to use type assertions to convert between these types:

bar = foo as number; /* convert to subtype */
foo = bar as number | string; /* convert to supertype (assertion not necessary but allowed) */

Similarly, { a: string, b: string } is a subtype of { a: string }. Any value that matches { a: string, b: string } (e.g. { a: "A", b: "B" }) also matches { a: string }, because it has an a property of type string.

In contrast, neither of { a?: string, b: string } or { a: string } is a subtype of the other. Some values (e.g. { b: "B" }) match only the former, and others (e.g. { a: "A" }) match only the latter.

If you really need to convert between unrelated types, you can work around this by using a common supertype (such as any) as an intermediate:

let foo = ({ a: "A" } as any) as { a?: string, b: string };

The relevant part in the spec is 4.16 Type Assertions:

In a type assertion expression of the form < T > e, e is contextually typed (section 4.23) by T and the resulting type of e is required to be assignable to T, or T is required to be assignable to the widened form of the resulting type of e, or otherwise a compile-time error occurs.

score:0

Well, this is only a theory but I have some code to back it up.

In your first example, when casting to AllRequired you're basically saying to the compiler the you know what you're doing and you'll set the value for all.b later on.

In the second example you think you're doing the same but the compiler probably thinks "hey, if he sets the optional property he must be setting a full object" and so it's confused as for why the value for some.b is missing from this full object.

This may sound a bit far fetched, but here:

let some2 = (all.a ? {a: "foo"} : {}) as SomeOptional

The error is gone if you use a condition because then (well, probably) some2.a is really optional.

Try it in playground.


Related Query

More Query from same tag