Cody Moncur
Cody Moncur

Reputation: 337

Cannot deduce type of const after explicit type check? (TS2349: Cannot invoke an expression whose type lacks a call signature.)

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

Answers (1)

Daniels
Daniels

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

Related Questions