I am trying to understand if this is the intended working mechanism of type assertion or a compiler bug.
This is the use case:
interface IExample {
one: string;
two: number;
}
interface IGeneric<T> {
example: [key: keyof T, someText: string][]; // tuple array
}
These are the 3 examples with their respective error messages. 1 and 2 seem to be working as expected, but I am surprised that the behaviour of example3 differs from example1?
// Type '"three"' is not assignable to type 'keyof IExample'.(2322)
let example1: IGeneric<IExample> = { example: [
['one', 'one'],
['three', 'three'],
]};
/*
Conversion of type '{ example: (["three", string] | ["four", string])[]; }' to type 'IGeneric<IExample>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
Types of property 'example' are incompatible.
Type '(["three", string] | ["four", string])[]' is not comparable to type '[key: keyof IExample, someText: string][]'.
Type '["three", string] | ["four", string]' is not comparable to type '[key: keyof IExample, someText: string]'.
Type '["four", string]' is not comparable to type '[key: keyof IExample, someText: string]'.
Type at position 0 in source is not compatible with type at position 0 in target.
Type '"four"' is not comparable to type 'keyof IExample'.
*/
let example2 = { example: [
['three', 'three'],
['four', 'four'],
]} as IGeneric<IExample>;
// no error ??
let example3 = { example: [
['one', 'one'],
['three', 'three'],
]} as IGeneric<IExample>;
CodePudding user response:
You should think of as
as nothing more than a way of suppressing type errors; you can use it when you know the type of something better than the compiler does, or when you want to lie to the compiler out of convenience. So getting no type error when you use as
in your third example is not a surprise; that's what as
is meant to do.
The real question is why your second example does give a type error. Essentially, it's because in your third example the inferred type has some overlap with the asserted type, whereas in your second example there is no overlap with the asserted type. The as
keyword is meant to tell the compiler that a value of one type is actually of a different type, but if the two types have no overlap at all, then the compiler still gives an error because what you're asserting is not just false, it's logically impossible.*
* Pedantic note: technically the two types don't exactly have no overlap at all, because an object with an empty array {example: []}
is assignable to both types. Still, the same rationale applies; the compiler is willing to "take your word for it" if the types have sufficient overlap, but will still give an error if the overlap is so small that you're probably making a mistake.
CodePudding user response:
Not a bug. See https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions
example3
's initializer type is a supertype of IGeneric<IExample>
, but example2
's is not.