Skip to content

Array.isArray type guard does not match arrays with readonly modifiers #55238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
crizzis opened this issue Aug 2, 2023 · 6 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@crizzis
Copy link

crizzis commented Aug 2, 2023

Bug Report

Array.isArray, when used on a type union, does not result in a positive type guard match on array types that are declared readonly

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about 'array', 'readonly'

⏯ Playground Link

Playground link with relevant code

🙁 Actual behavior

After testing a readonly T[] | V-typed variable with Array.isArray(), acessing V-exclusive properties without a cast still yields an error, as the LHS of the union type is not ruled out. ('Property does not exist on type readonly T[]')

In contrast, T[] | V - typed variables don't have this problem.

🙂 Expected behavior

The behaviour should be consistent for both readonly and non-readonly variants

@crizzis crizzis changed the title Array.isArray type assertion does not match readonly modifiers Array.isArray type guard does not match arrays with readonly modifiers Aug 2, 2023
@crizzis
Copy link
Author

crizzis commented Aug 2, 2023

Suggested solution (this would fix the scenario, not sure if it doesn't break sth else, though :P ):

interface ArrayConstructor { new(arrayLength?: number): any[]; new <T>(arrayLength: number): T[]; new <T>(...items: T[]): T[]; (arrayLength?: number): any[]; <T>(arrayLength: number): T[]; <T>(...items: T[]): T[]; isArray(arg: any): arg is any[] | readonly any[]; readonly prototype: any[]; }

(note the any[] | readonly any[])

@fatcerberus
Copy link

Duplicate of #53395, in which this is considered to be working as intended and I disagree 😄

@jcalz
Copy link
Contributor

jcalz commented Aug 2, 2023

Yeah, that one flew by me. I also disagree with that decision, I think. I find it difficult to worry about a non-Array object with all the properties of readonly any[], since I don't currently worry about non-Array objects with all the properties of any[]. Has anyone seen such a monster in the wild? Is there some real-world code we're worried about breaking?

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Aug 2, 2023
@RyanCavanaugh
Copy link
Member

Filing this under the "People generally say TypeScript should be more strict but specifically want possibly-wrong but convenient answers" folder 😉

@fatcerberus
Copy link

@RyanCavanaugh FWIW this issue is actually a good example of when it's more or less impossible for TypeScript to be sound - given how TypeScript works, x is T is fundamentally making an assertion about a type's structure, whereas Array.isArray() is a nominal check. There's an impedance mismatch there that can't really be reconciled1, hence your observation here.

Footnotes

  1. ...without introducing real nominal types anyway, but that's an entire can of worms unto itself.

@typescript-bot
Copy link
Collaborator

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

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Aug 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants