1

Let's say I want to verify that a variable is neither null or undefined.

I would genuinely do:

let foo: string | undefined | null;

if (foo !== undefined && foo !== null) {
  // foo is a string
}

Now, I would like to create a function that can do the same check on multiple variable at once.

I have tried:

const isSet = <T>(...values: T[]): values is Exclude<T, null | undefined>[] => {
  return values.every(value => value !== undefined && value !== null)
};

However, I get a Typescript error "A type predicate cannot reference a rest parameter", which make sense.

What do you suggest?

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
nsayer
  • 799
  • 2
  • 8
  • 21
  • shouldn't the return type of this function be "boolean"? – 8ns Feb 11 '22 at 15:55
  • 2
    @8ns, it does; this is known as *type guarding*. – Connor Low Feb 11 '22 at 16:06
  • Here is [a similar question](https://stackoverflow.com/questions/58636396/type-guard-that-asserts-all-rest-parameters-are-not-undefined). [See also](https://stackoverflow.com/questions/49684532/can-i-write-a-type-guard-that-asserts-multiple-invariants). – Connor Low Feb 11 '22 at 16:08
  • thanks @Connor Low I'll check it out! – 8ns Feb 11 '22 at 16:09

1 Answers1

0

You cannot write a type guard against multiple parameters. I would change your strategy to something like this:

function isSet<T,>(...values: T[]) : null | NonNullable<T>[] {
    return values.every(value => value !== undefined && value !== null) 
        ? values as NonNullable<T>[] 
        : null;
}

This is an instance where the compiler isn't smart enough to assert the type of values, so you have to give it a little help with as NonNullable<T>[]. Rather than return a boolean (or type guard), return the values you just verified.

Usage looks like this:

let a = null, b = undefined, c = 0;

// guarded: null | number[];
let guarded = isSet(a, b, c);

if (guarded) {
  // guarded: number[]
  let [x, y, z] = guarded;
}

Playground

Connor Low
  • 5,900
  • 3
  • 31
  • 52