Reputation: 30071
I have a utility function to check whether a variable is not null or undefined, and I want TypeScript to narrow down the input variable if it passes the check, for example:
public init(input?: string): void {
function isSpecified(input: any): boolean {
return (typeof input !== "undefined") && (input !== null);
}
if (isSpecified(input)) {
let copiedString: string = input; // <-- Error; input is still 'string | undefined'
}
}
As you can see TS is not removing the possibility of the string being undefined
even though the function makes that logically impossible. Is there a way I can get this function call to narrow down input
inside the if
block?
Upvotes: 2
Views: 1019
Reputation: 221402
You can use a generic type guard function:
public init(input?: string): void {
function isSpecified<T>(input: null | undefined | T): input is T {
return (typeof input !== "undefined") && (input !== null);
}
if (isSpecified(input)) {
let copiedString: string = input; // OK
}
}
Note that if TypeScript narrows down a variable's union type through narrowing during an assignment, isSpecified
will still not act as a type guard. For example, in the following code, TS will give an error:
someMethod = () => {
const foo: undefined | null | number = undefined;
if (isSpecified(foo)) {
this.nonNullValue = foo; // Error
}
};
In the above, foo
being assigned as undefined
narrows the variable type and the T
in isSpecified
is undefined
, so TS still doesn't treat foo
as definitely being specified. This can be worked around by using a type assertion (val as ...
) to prevent narrowing:
someMethod = () => {
//const foo = 123 as
// ... or:
const foo = undefined as
undefined | null | number;
if (isSpecified(foo)) {
this.nonNullValue = foo;
}
};
Upvotes: 6
Reputation: 95772
While the type guard function suggested in the other answers works well in many cases, in this case you have another much simpler option. Instead of checking for (typeof input !== "undefined") && (input !== null)
just inline a check for input != null
.
It's easy to forget that sometimes the type casting done by the double equal ==
and !=
can actually be useful:
function init(input?: string): void {
if (input != null) {
let copiedString: string = input; // <-- input is now 'string'
}
}
In javascript or typescript the following are all true
:
undefined == null
null == null
'' != null
Upvotes: 0
Reputation: 74705
Yes, you've basically just written a typeguard function without adding the typeguard.
Change:
function isSpecified(input: any): boolean
to:
function isSpecified(input: any): input is string
More generally, you can use a generic version of the same thing, as mentioned by Ryan:
function isSpecified<T>(input: null | undefined | T): input is T
Upvotes: 2