You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Array.isArray type narrows to any[] for ReadonlyArray<T> #17002
declareinterfaceT{foo: string;}constimmutable: T|ReadonlyArray<T>=[];if(Array.isArray(immutable)){constx=immutable;// Any[] - Should be ReadonlyArray<T>}constmutable: T|Array<T>=[];if(Array.isArray(mutable)){constx=mutable;// T[]}
Expected behavior: Should type narrow to ReadonlyArray<T>, or at the very least T[].
Actual behavior: Narrows to any[]. Doesn't trigger warnings in noImplicitAny mode either.
Joe Calzaretta (@jcalz) yes, I think these overloads should be in the standard library. There are a few other scenarios where ReadonlyArray isn't accepted where it should be (for example Array.concat).
but of course, since at runtime there's no way to tell the difference between ReadonlyArray and Array, you would have to be careful to use the right one in the right places in your code. ¯\_(ツ)_/¯
Vidar Tonaas Fauske (@vidartf) As instanceof works at runtime, I'm not sure that should narrow to ReadonlyArray. You're asking if the runtime representation of that is an array, which is true. So I'd expect it to narrow to Array<T>, not ReadonlyArray<T>.
Jinder Sidhu (@jinder) I didn't state it explicitly, but my code was meant to be based on yours (same variables and types), so it should already know that it was T | ReadonlyArray<T>. As such, instanceof should narrow it to ReadonlyArray<T>.
Reacted by Jinder Sidhu, meldridon and Alex von Brandenfels
letcommand: readonlystring[]|string;letcached_command: Record<string,any>;if(Array.isArray(command)){}else{cached_command[command]=1;// => Error: TS2538: Type 'readonly string[]' cannot be used as an index type.}
Just adding my 2c here that I would really appreciate for this to work natively. I think its a little unintuitive that Array.isArray doesn't catch a readonly [], although I understand that its not as simple as a one line fix :)
functionisReadonlyArray<T,Rest>(value: readonlyT[]|Rest): value is readonlyT[]{returnArray.isArray(value);}// or justconstisArray=Array.isArrayas<T,Rest>(value: readonlyT[]|Rest)=>value is readonlyT[];
This also works with unknown you don't want to use any or type assertions (due to linting rules or personal preference)
declare global {
interface ArrayConstructor {
isArray(arg: unknown): arg is readonly unknown[];
}
}
Imo this is the cleanest option because it only augments the type without declaring new variables, so output stays the same..
I like your version better for its simplicity.
The only minor difference I noticed is that if the input type is statically known to be non-array, the true branch won’t narrow to never, but instead to V & readonly unknown[].
That said, I personally avoid augmenting standard library types and prefer creating a dedicated helper. But this is mostly a matter of preference 😉
I've submitted a minimal fix for this in #63609 — a single additive overload in es5.d.ts that preserves readonly when the input type already carries it. Unlike approaches that change the narrowing result for unknown (which Sigurd Spieckermann (@sisp) correctly identified as a breaking change), this overload only matches when the input is already typed as readonly any[] | T, so unknown and mutable arrays continue to narrow exactly as they do today. The total change is one line in the lib plus a test file.
Activity
jcalz commentedon Jul 7, 2017
If you add the following declaration to overload the declaration of
isArray():the post-checked
mutableis still narrowed toT[]and the post-checkedimmutableis narrowed toReadonlyArray<T>as desired.I'm not sure if this or similar would be an acceptable addition to the standard typing libraries or not, but you can at least use it yourself.
jinder commentedon Jul 7, 2017
Joe Calzaretta (@jcalz) yes, I think these overloads should be in the standard library. There are a few other scenarios where ReadonlyArray isn't accepted where it should be (for example
Array.concat).vidartf commentedon Jul 10, 2017
The same narrowing problem also exists for this check:
I'm not sure if there exists a similar workaround for this.
jcalz commentedon Jul 10, 2017
I would probably do this if I wanted a workaround:
And then later
but of course, since at runtime there's no way to tell the difference between
ReadonlyArrayandArray, you would have to be careful to use the right one in the right places in your code. ¯\_(ツ)_/¯jinder commentedon Jul 10, 2017
Vidar Tonaas Fauske (@vidartf) As
instanceofworks at runtime, I'm not sure that should narrow toReadonlyArray. You're asking if the runtime representation of that is an array, which is true. So I'd expect it to narrow toArray<T>, notReadonlyArray<T>.vidartf commentedon Jul 10, 2017
Jinder Sidhu (@jinder) I didn't state it explicitly, but my code was meant to be based on yours (same variables and types), so it should already know that it was
T | ReadonlyArray<T>. As such,instanceofshould narrow it toReadonlyArray<T>.NN--- commentedon Sep 20, 2017
What is the best workaround here ?
I just ended up with ugly type assertion:
adrianheine commentedon Sep 17, 2018
I think this is fixed in 3.0.3.
jinder commentedon Sep 18, 2018
Adrian Heine né Lang (@adrianheine) doesn't seem to be for me.
adrianheine commentedon Sep 18, 2018
Oh, yeah, I was expecting the code in the original issue to not compile, bu that's not even the issue.
bluelovers commentedon May 17, 2019
paulmiu commentedon May 22, 2019
Temporary solution from Aleksey Lagashkin (@aleksey-l) (on stackoverflow) until the bug is fixed:
46 remaining items
A-EbrahimRSA commentedon Apr 4, 2025
Just adding my 2c here that I would really appreciate for this to work natively. I think its a little unintuitive that Array.isArray doesn't catch a readonly [], although I understand that its not as simple as a one line fix :)
jonlepage commentedon Apr 6, 2025
here my solution
enjoy:
playground
A-EbrahimRSA commentedon Apr 7, 2025
Thanks for that suggestion 👍 , but I've seen that workaround above in this thread.
I'd prefer to not resort to a workaround and rather use a neater solution in-which the in-built .isArray method handles this use case.
oconnorjoseph commentedon Apr 9, 2025
Our team just ran into this issue with the
readonlykeyword as well. We would very much appreciate for this to work natively.Remove any from Array.isArray, fix violations (#6760)
spalt08 commentedon Jan 22, 2026
Another workaround:
playground
cahnory commentedon Jan 22, 2026
Funny timing, I just stumbled upon this issue about 6 hours after your comment, here’s an alternative version of your workaround I ended up using:
niklasholm commentedon Feb 25, 2026
This also works with
unknownyou don't want to useanyor type assertions (due to linting rules or personal preference)Imo this is the cleanest option because it only augments the type without declaring new variables, so output stays the same..
slztfn commentedon Mar 17, 2026
I believe cahnory solution is the best one so far and it should be in the standard library
cahnory commentedon Mar 18, 2026
I like your version better for its simplicity.
The only minor difference I noticed is that if the input type is statically known to be non-array, the
truebranch won’t narrow tonever, but instead toV & readonly unknown[].That said, I personally avoid augmenting standard library types and prefer creating a dedicated helper. But this is mostly a matter of preference 😉
fix(types): ensure Array.isArray will not widen readonly arrays to any
daishuge commentedon Jul 5, 2026
I've submitted a minimal fix for this in #63609 — a single additive overload in
es5.d.tsthat preservesreadonlywhen the input type already carries it. Unlike approaches that change the narrowing result forunknown(which Sigurd Spieckermann (@sisp) correctly identified as a breaking change), this overload only matches when the input is already typed asreadonly any[] | T, sounknownand mutable arrays continue to narrow exactly as they do today. The total change is one line in the lib plus a test file.anderson-pete commentedon Jul 6, 2026
Dai Luo (@daishuge): I left a comment on your PR. Unfortunately, your fix would break a lot of existing code.
fix(config): fifth codex round — deep-readonly metadata, integer freq…