2

I have the below array which can have either be an object or undefined. Even after filtering the results to contain all defined values, typescript still says it could be undefined

interface Bob {
    name: string;
}

type BobDetails = undefined | Bob;

const myArray: BobDetails[] = [{ name:  "Bob"}, undefined];
myArray[0].name; // expected Object is possibly 'undefined'.(2532)

var filter = myArray.filter(arr => arr?.name);
filter[0].name; // still Object is possibly 'undefined'.(2532)

How do make Typescript know that filter[] will have only Bob[] and no undefined. I am aware we might be able to do with ! but I want the normal flow to realise it.

Typescript Playground Example

Nidhin Joseph
  • 9,981
  • 4
  • 26
  • 48

2 Answers2

4

To do that, you need to have your filter function be a custom type guard. Right now, arr => arr?.name is just a function that returns a string | undefined. You and i can figure out what implications that will have for the resulting array, but typescript needs a bit more.

If you change the function so that it returns a boolean, and you give it the special return type arr is Bob, then typescript knows that if true is returned, then it can deduce that arr is a Bob. And the filter method knows that this will then result in a Bob[]:

var filter = myArray.filter((arr): arr is Bob => arr?.name !== undefined);

Playground link

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • `Right now, arr => arr?.name is just a function that returns a string | undefined` how can this be possible? We are filtering the results right? – Nidhin Joseph Oct 05 '22 at 11:33
  • This is a neat solution although I guess its the same as `var filter = myArray.filter(arr => arr?.name) as Bob[];`. You're simply moving the cast to the type guard after all. –  Oct 05 '22 at 11:33
  • 2
    @NidhinJoseph The answer to that is probably : Typescript cannot infer this. [This](https://stackoverflow.com/questions/62032214/why-cant-typescript-infer-type-from-filtered-arrays) may answer that question also. –  Oct 05 '22 at 11:36
  • The type on `arr => arr?.name` is the same as the type on `arr => Math.random() > 0.5 ? 'foo' : undefined`, but only the first one should result in `filter` being a `Bob[]`. Using only the implicit type information, there isn't a way for typescript to know whether the array will be narrowed or not. So you need to use a more explicit type to give typescript more information. – Nicholas Tower Oct 05 '22 at 11:43
1

Imagine the array being: BobDetails[] = [undefined, undefined];

Then filter[0].name; will also be undefined. For simplicity you can check the length of filter as:

if(filter.length>0){ filter[0].name; ... your code}
  • How does this resolve the error? it still shows `Object is possibly undefined` –  Oct 05 '22 at 11:29