0

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;
}
1
  • 1
    You can't, TypeScript won't narrow the variables based on their use in an array, even if you explicitly make the some callback a type predicate. The destructuring's irrelevant (here's an actual minimal reproducible example: tsplay.dev/NDzK8W). Commented Dec 29, 2021 at 15:56

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.