Reputation: 337
I've run into a bizarre issue with TypeScript where it seems like I should be able to deduce that a constant is an array, and with that knowledge call array methods on it. However, TypeScript apparently isn't able to determine that a value is truly an array even after an explicit check.
Given:
type Maybe<T> = T | null | undefined;
type Value = string
| number
| string[]
| number[]
| Maybe<number | string>[];
I can call the .every
array method on a constant if I typecheck the constant thusly:
// This approach works fine:
const currVal: Maybe<Value> = perhapsSomeValueSomewhere;
if (Array.isArray(currVal)) {
const arr: any[] = currVal;
const isListOfStrings = arr.every((s) => typeof(s) === 'string');
// ... Do other stuff here
}
I feel like it should follow that the above can be reduced to:
// This approach results in an error:
const currVal: Maybe<Value> = perhapsSomeValueSomewhere;
const isListOfStrings = Array.isArray(currVal)
&& currVal.every((s) => typeof(s) === 'string');
// ... Do other stuff here
However this approach results in an error:
TS2349: Cannot invoke an expression whose type lacks a call signature. Type '((callbackfn: (value: string, index: number, array: string[]) => boolean, thisArg?: any) => boole...' has no compatible call signatures.
I can only assume this means that, along the way, TypeScript has lost the context that by && currVal.every((s) => ...
this point, currVal
is indeed an array of some sort.
Why would the latter result in an error when the two bits of logic seem rather comparable?
Upvotes: 0
Views: 103
Reputation: 161
In the first case you have an implicit type cast by assigning currVal to any[], in the second you have not cast the type to an array type. You can get this to work with the following explicit cast.
const isListOfStrings = Array.isArray(currVal) && (<Array<any>>currVal).every((s) => typeof (s) === 'string');
Upvotes: 1