Typescript: TS2322: Type 'null' is not assignable to type 'string | void'

Created on 27 Apr 2016  路  9Comments  路  Source: microsoft/TypeScript

TypeScript Version: nightly (1.9.0-dev.20160426)
EDIT: used flags: --strictNullChecks --noImplicitAny --noImplicitThis

const a1: string | void = "a";
const a2: string | void = undefined;
const a3: string | void = null; // error TS2322: Type 'null' is not assignable to type 'string | void'.

The documentation says that you can assign undefined or null to void:

Declaring variables of type void is not useful because you can only assign undefined or null to them

Is the documentation still correct?
It looks like we cannot assign null to void, so is void the same as undefined?

A related problem in my type guard function (simular to TS v1.8 sample):

function isVoid<T>(obj: T | void): obj is void {
  return obj === null || obj === undefined; // error TS2365: Operator '===' cannot be applied to types 'T | void' and 'null'.
  // return obj === undefined; // Is OK
}

Can someone clarify the differences/relations between: void, null, undefined and maybe also {} and any in TS v1.9+?

Question

Most helpful comment

then null is not in the domain of either string or void.

Quick background, if you are not using --strictNullChecks both null and undefined are in the domain of all types. so T | undefined | null is equivalent to T. if you are using --strictNullChecks however, that is not the case. T does not include null or undefined.

About the meanings, null is the type of the js value null. at runtime it is an object (i.e. typeof null === "object"), and it means the sentinel object. undefined is the type of the js value undefined which means uninitialized. so an uninitialized variable has type undefined for instance. an optional parameter is implicitly known to include the undefined type, e.g. x?: string, here x is of type string|undefined. void is the absence of a type, you could think of it as another name to undefined. the main use for void is to distinguish a function that is expected to returns nothing (void) from one that would return a value but possibly undefined (this way you can not assign ()=>void to something like ()=>string|undefined by mistake).

So in short, only use void in return types of functions, to mean nothing is returned. use undefined to mean either uninitialized, or the js undefined value. use null to mean the js value null.

hope that helps.

All 9 comments

are you using --strictNullChecks ?

Yes

then null is not in the domain of either string or void.

Quick background, if you are not using --strictNullChecks both null and undefined are in the domain of all types. so T | undefined | null is equivalent to T. if you are using --strictNullChecks however, that is not the case. T does not include null or undefined.

About the meanings, null is the type of the js value null. at runtime it is an object (i.e. typeof null === "object"), and it means the sentinel object. undefined is the type of the js value undefined which means uninitialized. so an uninitialized variable has type undefined for instance. an optional parameter is implicitly known to include the undefined type, e.g. x?: string, here x is of type string|undefined. void is the absence of a type, you could think of it as another name to undefined. the main use for void is to distinguish a function that is expected to returns nothing (void) from one that would return a value but possibly undefined (this way you can not assign ()=>void to something like ()=>string|undefined by mistake).

So in short, only use void in return types of functions, to mean nothing is returned. use undefined to mean either uninitialized, or the js undefined value. use null to mean the js value null.

hope that helps.

Also please note that this feature landed in master a few weeks ago, and we have not updated the documentation to reflect it yet.

That makes it more clear.

I was working on an TS v1.8 project and I wanted to express nullability in the type by using the void type.
That didn't work as fluent as expected with the required type guard functions, so I tried the latest nightly TS v1.9 that solves non-nullability much better!

So without using --strictNullChecks I needed

function isVoid<T>(obj: T | void): obj is void {
  return obj === null || obj === undefined;
}

and when using --strictNullChecks the meaning of void changes (_here was my confusion_)

function isVoid<T>(obj: T | void): obj is void {
  return obj === undefined;
}

Although more correct, I think this can cause problems:

Imagine that the above isVoid sample is created in an external library (external.ts) that is developed with TS and --strictNullCheck flag. The implementation will not check for null, else you will get a compile error TS2365: Operator '===' cannot be applied to types 'T | void' and 'null'.

I then make that external library (external.js) available in my web page and I only have the declaration file (external.d.ts) in my project with the isVoid<T>(obj: T | void): obj is void signature.

If I use that external function isVoid with signature T | void in my project without --strictNullChecks or an older TS version, I can pass null to that function and it will not behave as expected. isVoid will tell me that null is not void. This can cause now null references exceptions.

So I think mixing libraries with/without --strictNullChecks can be dangerous when void is used not as function return type. Is this correct?

Correct. People have repeatedly tried to use | void to hack in nullability analysis and we've tried to warn them that this would not be a well-supported thing, but it didn't really stick.

I started using it after I saw this Maybe<T> sample in the What's new in Typescript.
Maybe good to use another example for union types.

Will update the docs to remove this pattern.

I resolve it with this code

const userJson = localStorage.getItem('currentUser');
this.currentUser = userJson !== null ? JSON.parse(userJson) : new User();
Was this page helpful?
0 / 5 - 0 ratings