EDIT: updated to reflect fixing a typo in question: The question is a little confusing, since your notNullOrUndefined()
doesn't check a.foo
at all, so it's not surprising that those would be different.
Note that with --strictNullChecks
on, you have defined len()
so that the a
parameter is an A
, and therefore cannot be null or undefined. So you don't have to check a
itself inside the len()
function implementation; instead you need to make sure that anything you pass to len()
is a valid A
. So notNullOrUndefined()
is kind of a bad name, since you're checking the foo
value of the parameter, not the parameter itself. Feel free to change it to something like fooPropertyIsNotNull()
; I will leave it for now.
The main issue here is that TypeScript recognizes that if (a.foo != null) { ... }
is a type guard, and narrows a.foo
to string
inside the { ... }
clause. But type guards do not propagate out of functions automatically, so TypeScript doesn't understand that notNullOrUndefined()
itself acts as a type guard.
Luckily, this issue is common enough that TypeScript provides user-defined type guards: if you have a function that returns a boolean
which narrows the type of one of its parameters, you can change the boolean
return type to a type predicate, using the x is T
syntax. Here it is for notNullOrUndefined()
:
const notNullOrUndefined = (a: A): a is { foo: string } => {
return a.foo != null;
}
So the function signature says: if you pass in an A
, it will return a boolean value. If it returns true
, then the passed-in parameter is narrowed to { foo: string }
. Now you will get no errors, as you wanted:
interface A {
foo: string | undefined;
}
const notNullOrUndefined = (a: A): a is { foo: string } => {
return a.foo != null; // checking a.foo
}
const len = (a: A): number => {
if (notNullOrUndefined(a)){
return a.foo.length; // okay
}
return 0;
}
Hope that helps, good luck!