Not planned
Description
π Search Terms
isArray
π Version & Regression Information
Bug occurs in 5.3.3
β― Playground Link
π» Code
interface MyInterface {
someProperty: string;
}
const myConst: MyInterface | Array<MyInterface> = { someProperty: "hello" };
if (Array.isArray(myConst)) {
// const myConst: MyInterface & any[]
myConst[0].thisPropertyDoesNotExist = "world"; // Ew.
myConst.someProperty = "world"; // Ew.
}
π Actual behavior
It compiles.
π Expected behavior
Accessing thisPropertyDoesNotExist should be an error.
Accessing someProperty when isArray says it's an array should be an error.
Additional information about the issue
No response
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
jcalz commentedon Dec 21, 2023
Duplicate ofRelated to #33700 probably (edit: @fatcerberus is right)Also: Issue title drops the object of the
fatcerberus commentedon Dec 21, 2023
@jcalz #33700 is definitely related, but see below.
You only lose the array type here because the value is narrowed to
MyInterface
on assignment, so the type guard has to remanufacture the array type from whole cloth. If you actually have aMyInterface | MyInterface[]
then it works as expected:betterIsArray()
doesn't fare much better in this situation - it narrows the value toMyInterface & MyInterface[]
, which is clearly not what you want (and allows the assignment tomyConst.something
to succeed). That's not observable in your playground as-is because for some reason (this might be a separate bug) it gets re-widened toMyInterface | MyInterface[]
after the first check.RyanCavanaugh commentedon Dec 21, 2023
This is correct behavior. On this line you said
TypeScript can see this line of code, and can see that
{ someProperty: "hello" }
is aMyInterface
, not an array.So then when you write
The only situation where this is
true
is one where whatever initializedmyConst
was also an array. What kind of array? Well, we don't know, so it'sany[]
.If the initializer of
myConst
isn't statically-determined to beMyInterface
, then you see the error on line 9 as expected.fatcerberus commentedon Dec 21, 2023
@RyanCavanaugh Is it a bug that
myConst
widens to the declared type after theisArray
check?geon commentedon Dec 21, 2023
But we do know. The array is typed before the if, and elements should have the same type inside the if.
jcalz commentedon Dec 21, 2023
No,
in
operator narrowing has apparently precluded the possibility ofArray<MyInterface>
.fatcerberus commentedon Dec 21, 2023
The declared type, yes, but the value is narrowed on assignment (TS knows that you didn't assign an array so removes that possibility from the union) so the type system has essentially "forgotten" about the array type by the time you do the
isArray
check.typescript-bot commentedon Dec 24, 2023
This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes.