I have a collection of data that looks like this:
interface Item { name: "one" | "two"; data: string; } const namedItems: Item[] = [ { name: "one", data: "some data one", }, { name: "two", data: "some data two", }, ];
Each item has a name and the value can either be "one" or "two".
Then running an array find on this:
const getData = (query: "one" | "two") => namedItems.find((item): boolean => query === item.name).data;
Throws a typescript error "Object is possibly 'undefined'". Which seems to be because of the fact that finds can possibly not find something but in my example you're only allowed to look for "one" or "two" which would always return a result.
How do I get typescript to know it will always return a result in the find?
The "Object is possibly 'undefined'" error occurs when we try to access a property on an object that may have a value of undefined . To solve the error, use the optional chaining operator or a type guard to make sure the reference is not undefined before accessing properties.
We can use typeof or '==' or '===' to check if a variable is null or undefined in typescript.
To avoid undefined values when using or accessing the optional object properties, the basic idea is to check the property value using an if conditional statement or the optional chaining operator before accessing the object property in TypeScript.
TypeScript has two special types, null and undefined , that have the values null and undefined respectively. We mentioned these briefly in the Basic Types section. By default, the type checker considers null and undefined assignable to anything. Effectively, null and undefined are valid values of every type.
Explanation
The reason you are experiencing this is the type signature for Array.prototype.find
:
find(predicate: (value: T, index: number, obj: T[]) => boolean, thisArg?: any): T | undefined;
As you can see, it always returns T | undefined
. This makes a lot of sense because a collection defined as Item[]
can contain an arbitrary number of items — including 0. In your case, the collection is complete, but on the type level it's no different than [{ name: "one", data: "some data one" }]
or even []
.
In order to access item.data
in a type-safe manner, TypeScript will require you to double check whether the result is indeed found.
const lookup = namedItems.find(item => item.name === 'one'); if (lookup === undefined) { throw new TypeError('The value was promised to always be there!'); } console.log(lookup.data);
Solution
Since this can become cumbersome in the long run, you may find it useful to create a helper function for this kind of scenarios.
function ensure<T>(argument: T | undefined | null, message: string = 'This value was promised to be there.'): T { if (argument === undefined || argument === null) { throw new TypeError(message); } return argument; }
Usage:
const getData = (query: "one" | "two") => ensure(namedItems.find(item => query === item.name)).data
If you're absolutely sure that you'll always get a match then you can tell TS so:
const getData = (query: "one" | "two") => (namedItems.find((item) => query === item.name) as Item).data;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With