Skip to content

Issues around casting tuples to arrays #62177

Closed as not planned
Closed as not planned

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

https://www.typescriptlang.org/play/?ts=6.0.0-dev.20250802#code/JYOwLgpgTgZghgYwgAgPICMBWEFgDLCRRwA2yA3gFDLIDaA1hAJ4BcyAzmFKAOYC6bOCCYBuSgF9KlACY4ScKCgTz27ZAAUA9pwrVkwaWxABXALbpoYmmEIkIbTtxA8xkmXIVLNIHaAAOxmAAjGwAFBjYuAREpMgAPsgmJCQAlLR8YgjeOghwnBDSQcgAvPogAcHIeRraYOkiyAD0jcia9FKyyp7IWT5gZRUATGy0ETj4hNCxCUkkADRoWOPRU2QzxskZlL05eZDSgyUDgYfVWpz1TS3QUJpQ7AvogchgABYoN3f6aiDZED7AXJkHjAABu-xe72Qpn+Nm8BReTD8EDU3XBUCY0OMCFePU0pj8Cjg6DsyAAtFU1OxXpoAO4gZAWEh0joeRR4vrHMAAZjYYyik2Ia0SGxImWy-Vy+Wk3KO-kCsrOtQazVa7SAA

💻 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.

Activity

  1. changed the title [-]Issues around casting arrays to tuples[/-] [+]Issues around casting tuples to arrays[/+] on Aug 2, 2025
  2. RyanCavanaugh commented on Aug 20, 2025

    @RyanCavanaugh
    Member

    If there's some concrete code mistake making this happen I would look at the PR, but the repro as given doesn't really seem compelling. There is always going to be some gray area in what's an acceptable cast and what isn't. It doesn't seem worth dealing with the inevitable "oh no we tried to tweak something minor and it broke something actually important" fallout from trying to change this for the sake of a repro no one actually came forward with.

  3. Andarist commented on Aug 21, 2025

    @Andarist
    ContributorAuthor

    Fwiw, I found this because it's the reason behind a regression introduced by one of my PRs. The code sample there is more complex and more real-life.

  4. typescript-bot commented on Aug 24, 2025

    @typescript-bot
    Contributor

    This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  5. locked as resolved and limited conversation to collaborators on Oct 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions