Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A "not null" type guard resolves to "never" in the else-branch

I created a type guard which checks if the argument is exactly null:

function isNotNull<T> (arg: T): arg is Exclude<T, null> {
  return arg !== null
}

When I test it, the then-branch works correctly: it strips away the null from the type.

const value: string | null = 0 as any
if (isNotNull(value)) {
  // value is of type `string` here
}

However, it becomes never in the else-branch.

const value: string | null = 0 as any
if (isNotNull(value)) {
  // `value` is of type `string` here
} else {
  // `value` is of type `never` here
}

I'd like it to resolve to null in the else-branch.

How can I achieve this?

like image 918
Lazar Ljubenović Avatar asked Aug 31 '25 01:08

Lazar Ljubenović


1 Answers

The problem here is the assignment:

const value: string | null = 0 as any

The compiler knows that value is a constant, so it can never be null.

It doesn't get much better if you use let:

let value: string | null = 'foo';
if (isNotNull(value)) {
  // `value` is of type `string` here
} else {
  // `value` is of type `never` here
}

Again the typescript compiler knows that the value is not null.

But if assign something where Typescript can't infer a constant value then you will have your expected string in the if branch and null in the else:

function isNotNull<T> (arg: T): arg is Exclude<T, null> {
  return arg !== null
}

let value: string | null = foo();

if (isNotNull(value)) {
  // `value` is of type `string` here
} else {
  // `value` is of type `null` here
}

function foo(): string | null {
    return "foo"
}

Also, even this last example only works if you have the strictNullChecks compiler option set true. If you haven't enabled strictNullChecks you cannot exclude null from type and the if branch will be string which is the same as string | null so that leaves only never for the else.

Edit: The reason why this doesn't work when strictNullChecks is off is quite interesting. In that case the types string | null and string are identical. That means value actually has the type string and Exclude<string, null> is simply string (and therefore still includes null), so the else clause is left with type never for value.

like image 58
Duncan Avatar answered Sep 02 '25 14:09

Duncan