Skip to content

fix(lib): preserve readonly narrowing in Array.isArray type guard#63609

Open
daishuge wants to merge 1 commit into
microsoft:mainfrom
daishuge:fix/array-isarray-readonly-narrowing
Open

fix(lib): preserve readonly narrowing in Array.isArray type guard#63609
daishuge wants to merge 1 commit into
microsoft:mainfrom
daishuge:fix/array-isarray-readonly-narrowing

Conversation

@daishuge

@daishuge daishuge commented Jul 5, 2026

Copy link
Copy Markdown

@
Fixes #17002

Problem

Array.isArray() narrows to arg is any[], which silently drops the readonly modifier when the input type already carries it:

function f(x: readonly string[] | string) {
    if (Array.isArray(x)) {
        x; // narrowed to string[] — readonly is silently dropped
        x.push("oops"); // no error, but the value was declared readonly
    }
}

This has been open for 9 years with significant community interest (64 comments).

Fix

One additive overload in src/lib/es5.d.ts:

isArray(arg: readonly any[] | any): arg is readonly any[];  // NEW
isArray(arg: any): arg is any[];                            // existing, unchanged

Overload resolution picks the first matching signature, so:

Input type Matched overload Narrowed to
readonly string[] | string new (1st) readonly string[]
string[] | string existing (2nd) string[]
unknown existing (2nd) any[]

Backward compatibility

A concern raised in #17002 (by @sisp) is that changing isArray to return readonly unknown[] for unknown inputs would be a breaking change. This PR does not have that problem — the new overload only matches when the input type is already readonly any[] | T. For unknown, any, or any mutable array type, overload resolution falls through to the existing isArray(arg: any): arg is any[] signature, so behavior is identical to today.

Scope

  • 1 line added to src/lib/es5.d.ts (additive overload)
  • 1 test file added: tests/cases/compiler/arrayIsArrayReadonlyNarrowing.ts
  • No changes to checker logic, no changes to existing overload behavior
  • Zero risk to existing code — the only new behavior is preserving readonly when it was already there

Submitted as a draft to get early feedback on whether this overload shape is the right approach, or whether the team prefers handling this differently (e.g. conditional type, checker change).
@

Adds an overload to ArrayConstructor.isArray that narrows
readonly array types correctly, fixing a 9-year-old issue where
Array.isArray() would lose readonly type information.

Fixes microsoft#17002

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Jul 5, 2026
@daishuge

daishuge commented Jul 5, 2026

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@daishuge daishuge marked this pull request as ready for review July 5, 2026 09:11
Copilot AI review requested due to automatic review settings July 5, 2026 09:11
@typescript-automation

Copy link
Copy Markdown

The TypeScript team hasn't accepted the linked issue #17002. If you can get it accepted, this PR will have a better chance of being reviewed.

1 similar comment
@typescript-automation

Copy link
Copy Markdown

The TypeScript team hasn't accepted the linked issue #17002. If you can get it accepted, this PR will have a better chance of being reviewed.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to fix long-standing narrowing behavior for Array.isArray so that when the input is already a readonly array union (e.g. readonly T[] | U), the type guard preserves readonly instead of narrowing to a mutable any[].

Changes:

  • Adds an overload to ArrayConstructor.isArray in src/lib/es5.d.ts intended to preserve readonly narrowing.
  • Adds a new compiler test case covering readonly vs mutable array narrowing and the unknown case.

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/lib/es5.d.ts Adds a new Array.isArray overload intended to preserve readonly array narrowing.
tests/cases/compiler/arrayIsArrayReadonlyNarrowing.ts Introduces a compiler test case for the intended narrowing behavior.

@@ -0,0 +1,26 @@
// @strict: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Array.isArray type narrows to any[] for ReadonlyArray<T>

2 participants