-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Description
π Search Terms
comparable relation casting comparison equality array tuple properties index infos
π Version & Regression Information
- This is the behavior in every version I tried
β― Playground Link
π» Code
interface ObjectLiteral {
[key: string]: any;
}
declare class Post {
id: number;
title: string;
}
declare const input1: (ObjectLiteral | null)[];
const casted1 = input1 as Post[]; // ok
declare const input2: [ObjectLiteral | null, ObjectLiteral | null];
const casted2 = input2 as Post[]; // errors, but the error is nonsensical given the mentioned types are very much comparable - as shown below
declare const input3: ObjectLiteral | null;
const casted3 = input3 as Post; // ok
π Actual behavior
the second cast fails and the user is left with a very confusing error given the third cast suceeds
π Expected behavior
I'd expect the second cast to succeed
Additional information about the issue
In the compiler's code we can find this definition:
A type S is comparable to a type T if some (but not necessarily all) of the possible values of S are also possible values of T.
Under this definition, this cast should be allowed:
const value = [
{ id: 1, title: "foo" },
{ id: 2, title: "bar" },
] as const satisfies any[];
const input2: [ObjectLiteral | null, ObjectLiteral | null] = value; // ok
const castTarget2: Post[] = value; // ok
More than that, some other related cases don't seem to adhere to this definition:
type PoorMansTuple = {
[K: number]: { x: number };
0: { x: number };
};
type PoorMansArray = {
[K: number]: { x: number; y: number };
};
declare const tupleLike: PoorMansTuple;
declare const arrayLike: PoorMansArray;
// Conversion of type 'PoorMansTuple' to type 'PoorMansArray' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
// 'number' index signatures are incompatible.
// Property 'y' is missing in type '{ x: number; }' but required in type '{ x: number; y: number; }'.(2352)
tupleLike as PoorMansArray;
// Conversion of type 'PoorMansArray' to type 'PoorMansTuple' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
// Property '0' is missing in type 'PoorMansArray' but required in type 'PoorMansTuple'.(2352)
arrayLike as PoorMansTuple;
In those cases, the reported errors are also confusing. At the very least, a cast between types mentioned by the error on tupleLike as PoorMansArray
is allowed. A missing property like it doesn't make a cast invalid.
This also extends to comparisons, given they also use comparable relation:
const x: [{ x: number; y: number }] = [{ x: 123, y: 456 }];
const tuple: [{ x: number }] = x; // ok
const array: { x: number; y: number }[] = x; // ok
// This comparison appears to be unintentional because the types '[{ x: number; }]' and '{ x: number; y: number; }[]' have no overlap.(2367)
if (tuple === array) {}
Thanks to @LukeAbby for discussing this stuff with me.