Bobby Tables
Bobby Tables

Reputation: 3013

Typescript check multiple variable for undefined

I have the following code block

const { att: { p, r } = {}, dir: { hdg } = {} } = record;

if ([p, r, hdg].some((e) => e === undefined)) {
    return null;
}
//TS2532: Object is possibly 'undefined'.
const heading = hdg.toValue("rad");

I destructure an object and then I check if any of the properties are undefined, if any of them is I exist the function, otherwise I continue the execution.

Even after the guard clause typescript still complains the object might be undefined.

How can I write this guard clause to make the compiler happy knowing the any of the fields can be 0 or booleans, except

if (p === undefined || r === undefined || hdg === undefined) {
    return null;
}

Upvotes: 0

Views: 1461

Answers (1)

Guerric P
Guerric P

Reputation: 31815

The best thing you could do is to use an Array and a type predicate like this:

type SomeType = { toValue(s: string): unknown };

function test(p?: SomeType, r?: SomeType, hdg?: SomeType) {

    const toCheck = [p, r, hdg];

    if (checkForNulls(toCheck)) {
        const [p, r, hdg] = toCheck;
        const heading = hdg.toValue("rad");
    }
}

function checkForNulls<T>(args: (undefined | T)[]): args is T[] {
    return args.every(e => e !== undefined)
}

TypeScript playground

One downside of this solution is that it widens every Array item type to an union type of all the actual items so it only works when the variables to check are of the same type. Otherwise the original types are lost.

Here is another solution that allows to recover original types. It works with function overload and tuples, but it requires to write as many overloads as needed usages:

type SomeType = { toValue(s: string): unknown };

function test(p?: boolean, r?: number, hdg?: SomeType) {

    const toCheck = [p, r, hdg] as const;

    if (checkForNulls(toCheck)) {
        const [p, r, hdg] = toCheck;
        const heading = hdg.toValue("rad");
    }
}

function checkForNulls<T>(args: readonly [T | undefined]): args is [T]
function checkForNulls<T, U>(args: readonly [T | undefined, U | undefined]): args is [T, U]
function checkForNulls<T, U, V>(args: readonly [T | undefined, U | undefined, V | undefined]): args is [T, U, V]
function checkForNulls<T>(args: readonly (undefined | T)[]): args is T[] {
    return args.every(e => e !== undefined)
}

TypeScript playground

Upvotes: 2

Related Questions