Search code examples
javascriptangulartypescriptnullable

Difference between Union Types with Null and Nullable Object declaration in Typescript


I recently found a bit of typescript code in a bigger angular project, that had a Bitwise-OR/Pipe-Symbol within its object declaration. Like this:

dataSource: FileSource | null;

In my understanding it is an object of the type FileSource that is also nullable.

dataSource = null; // Works
dataSource = new FileSource... // Works
dataSource = 32; // Error


I also found out that you can declare an object with a whole set of Datatypes like this:

myVariable: number | string | null;

Now to my actual question: I can as well declare an object with a question mark as a symbol for nullable. Is there any difference between these two declarations?

myVariable: FileSource | null;
mySecondVariable?: FileSource;

If there is no difference between these two, would you consider it a bad practice, as it is not common in other languages and no valid javascript code?

BTW: In Javascript this:

myVariable: Number | null;
myVariable = "Hello World";

would be fine.

My focus is the nullability of an object and how these declarations differ


Solution

  • Is there any difference between these two declarations?

    Yes, particularly with strict null checks. A property with a union type (the | symbol) is required to be present with a value that matches one of the types.

    An optional property (declared with ?) is just that: Optional. The object isn't required to have it at all. Although that said, at the moment (at least), TypeScript treats prop?: X exactly like prop: X | undefined; see this issue helpfully pointed out by jcatz.

    Without strict null checks, this is fine:

    type A = {
        dataSource: Date | null
    };
    type B = {
        dataSource?: Date
    };
    
    const a: A = { dataSource: null }; // Works
    const b: B = { dataSource: null }; // Also works
    

    With strict null checks, the second is wrong:

    type A = {
        dataSource: Date | null
    };
    type B = {
        dataSource?: Date
    };
    
    const a: A = { dataSource: null }; // Works
    const b: B = { dataSource: null }; // Error: Type 'null' is not assignable to type 'Date | undefined'.
    

    Live Example in the Playground

    Similarly, assigning undefined would be fine without strict null checks, but with them, it's an error in the union type case:

    type A = {
        dataSource: Date | null
    };
    type B = {
        dataSource?: Date
    };
    
    const a: A = { dataSource: undefined }; // Error: Type 'undefined' is not assignable to type 'Date | null'.
    const b: B = { dataSource: undefined }; // Works
    

    Live Example in the Playground