Why is Typescript not detecting that I’ve checked only part of unknown type, and not it’s nested properties thus resulting in runtime error?
interface User {
name: string,
age: number
}
const externalData: unknown = [1, 2, 3];
(Array.isArray(externalData) ? externalData : [])
.reduce<User[]>((acc, curr) => {
return [
...acc,
{
name: curr.some.nested.property, // not detecting that these properties may not exist on type 'any'
age: curr.some.nested.another.propety // not detecting that these properties may not exist on type 'any'
}
]
}, []);
2
Answers
noImplicit
will yell at you when there are no typings.For example
function (foo) { ... }
,foo
has no type so it’s implicitlyany
.In your case
(Array.isArray(externalData) ? externalData : [])
is inferred toany
.because the empty array is
any[]
.It looks like TypeScript is narrowing your
unknown
externalData
toany[]
whenArray.isArray(externalData)
is true. (I would have expectedunknown[]
.) Here are a couple of simpler tests:Notice how it’s clearly the true part of the conditional that’s contributing the
any[]
type to the union.Just going by the documentation (I don’t have a deep history in this part of TypeScript, or really any part), that would seem to be a case that should be caught:
I don’t see any issue for this in the issue list, although there are several outstanding issues with the result of narrowing array types (such as this one where
ReadonlyArray<T>
is narrowed toany[]
).Above my pay grade whether it’s a bug or limitation, but it’s that narrowing that appears to be what isn’t getting caught.