468

I have an interface in TypeScript.

interface Employee{
    id: number;
    name: string;
    salary: number;
}

I would like to make salary as a nullable field (Like we can do in C#). Is this possible to do in TypeScript?

Michael
  • 8,362
  • 6
  • 61
  • 88
Amitabh
  • 59,111
  • 42
  • 110
  • 159

12 Answers12

423

All fields in JavaScript (and in TypeScript) can have the value null or undefined.

You can make the field optional which is different from nullable.

interface Employee1 {
    name: string;
    salary: number;
}

var a: Employee1 = { name: 'Bob', salary: 40000 }; // OK
var b: Employee1 = { name: 'Bob' }; // Not OK, you must have 'salary'
var c: Employee1 = { name: 'Bob', salary: undefined }; // OK
var d: Employee1 = { name: null, salary: undefined }; // OK

// OK
class SomeEmployeeA implements Employee1 {
    public name = 'Bob';
    public salary = 40000;
}

// Not OK: Must have 'salary'
class SomeEmployeeB implements Employee1 {
    public name: string;
}

Compare with:

interface Employee2 {
    name: string;
    salary?: number;
}

var a: Employee2 = { name: 'Bob', salary: 40000 }; // OK
var b: Employee2 = { name: 'Bob' }; // OK
var c: Employee2 = { name: 'Bob', salary: undefined }; // OK
var d: Employee2 = { name: null, salary: 'bob' }; // Not OK, salary must be a number

// OK, but doesn't make too much sense
class SomeEmployeeA implements Employee2 {
    public name = 'Bob';
}
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • 80
    Looks like [strictly nullable types and strict null-checks](https://github.com/Microsoft/TypeScript/pull/7140) have been implemented and will arrive with Typescript 2.0! (or `typescript@next` now.) – mindplay.dk Jun 22 '16 at 14:36
  • are you sure about var c in the first example? It seems to me that var b and var c are the same there. – martinp999 Feb 03 '19 at 23:06
  • In order to set null or undefined value without compilation error, the tsconfig "strict" option must be removed or equals to "false" `"strict" : false` – Nicolas Janel Mar 19 '19 at 08:38
  • 24
    This is incorrect. JS distinguish between null and undefined. Correct code should be `salary:number|null;` If you do `salary?:number; salary = null;` You will get an error. However, `salary = undefined;` will work just fine in this case. Solution: use Union i.e. '|' – Ankur Nigam Apr 21 '20 at 06:19
  • 10
    (Downvoting as it is now obsolete / not good practice.) – ToolmakerSteve Oct 26 '20 at 21:09
  • 1
    Huh why not `| null`. You say yourself that undefined isn't the same as null. Optional is undefined, so this doesn't the answer at all? – html_programmer Aug 18 '21 at 23:12
  • I think this is not the answer to the question. We are not talking about Javascript. Besides null <> undefined – windmaomao Oct 12 '22 at 13:38
  • @mindplay.dk it does not seem to be default behaviour in typescript 4.9 – theonlygusti Jan 27 '23 at 01:07
324

To be more C# like, define the Nullable type like this:

type Nullable<T> = T | null;

interface Employee{
   id: number;
   name: string;
   salary: Nullable<number>;
}

Bonus:

To make Nullable behave like a built in Typescript type, define it in a global.d.ts definition file in the root source folder. This path worked for me: /src/global.d.ts

Tim Santeford
  • 27,385
  • 16
  • 74
  • 101
  • 5
    Using this breaks the auto-completion of object properties. For example if we have `emp: Partial`, we can do `emp.id` or `emp.name` etc but if we have `emp: Nullable`, we can't do `emp.id` – Yousuf Khan Apr 17 '20 at 14:04
  • 8
    @YousufKhan That is true. That's probably because since `emp` is potentially nullable, the `id` property might be invalid. To make the code robust, you should probably use an `if` block to check for null first, like this: `if (emp) { console.log(emp.id); }` If you use such an `if`-block, the TypeScript compiler and the editor "see" that the object inside the block is not null and thus will not generate errors and allow auto completion inside the if-block. (It works well in my Visual Studio 2019 editor and I assume it will work in Visual Studio Code too. But I don't know about other editors.) – Bart Hofland Aug 12 '20 at 05:57
  • 1
    @YousufKhan . . . When using `emp: Partial`, the resulting type contains all properties from `Employee`, but those properties will be nullable. (Well, `undefined`able might be the more appropriate term here.) So all properties of `emp` are available, but nullable. When using `emp: Nullable`, the `emp` variable itself is nullable. If it is not null, it should be a valid full `Employee` instance. You could combine those as well: `emp: Nullable>`. In that case, `emp` is nullable itself, but when not null, its properties can all be nullable as well. – Bart Hofland Aug 12 '20 at 06:06
  • Could you add Value and HasValue too like Nullable in C#? A generic class could probably be used but you will have to instantiate it too instead of just using a type declaration. – Tore Aurstad Jul 01 '21 at 13:22
  • Maybe even `type _Nullable = T | null | undefined;`? That's the vuefire definition – Boern Feb 10 '23 at 14:59
249

Union type is in my mind best option in this case:

interface Employee{
   id: number;
   name: string;
   salary: number | null;
}

// Both cases are valid
let employe1: Employee = { id: 1, name: 'John', salary: 100 };
let employe2: Employee = { id: 1, name: 'John', salary: null };

EDIT : For this to work as expected, you should enable the strictNullChecks in tsconfig.

i.am.michiel
  • 10,281
  • 7
  • 50
  • 86
bjaksic
  • 3,003
  • 1
  • 10
  • 12
  • 21
    If you use --strictNullChecks (which you should), this is a valid solution. I would not use it in favour of optional members, since it forces you to add an explicit null on all literal objects, but for function return values, it is the way to go. – geon Nov 30 '16 at 07:57
  • 1
    To clarify @geon's suggestion: an `optional property` of an interface is declared with a `?` *on the property name*. `salary?: number` means that `salary` can be omitted, or equivalently given value `undefined`, but cannot give it value `null`. [Good demonstration of different declarations using optional and/or null](https://stackoverflow.com/a/57243788/199364). – ToolmakerSteve Oct 26 '20 at 21:54
  • @ToolmakerSteve Yes. Using `salary: number | null` or `salary: number | undefined` is still great, because it forces you do set it to something, even if that something is `undefined`. It is easy to forget otherwise. – geon Nov 12 '20 at 16:51
70

Just add a question mark ? to the optional field.

interface Employee{
   id: number;
   name: string;
   salary?: number;
}
Miguel Ventura
  • 10,344
  • 2
  • 31
  • 41
  • 99
    As Ryan pointed out... ? means optional in typescript, not nullable. Without ? means the var must be set to a value including null or undefined. With ? you can skip the whole declaration-thingy. – He Nrik Apr 02 '15 at 11:09
  • 5
    Thank you! I googled for "typescript optional value" so this is **exactly** what I was looking for. – Fellow Stranger Oct 15 '19 at 14:38
  • 3
    As per @HeNrik's comment: This is an answer to a slightly different question than was asked. Since 2.0 in 2016, [Here is a good demonstration of all possible combinations of optional / undefined / null when declaring a type](https://stackoverflow.com/a/57243788/199364). – ToolmakerSteve Oct 26 '20 at 21:58
34

You can just implement a user-defined type like the following:

type Nullable<T> = T | undefined | null;

var foo: Nullable<number> = 10; // ok
var bar: Nullable<number> = true; // type 'true' is not assignable to type 'Nullable<number>'
var baz: Nullable<number> = null; // ok

var arr1: Nullable<Array<number>> = [1,2]; // ok
var obj: Nullable<Object> = {}; // ok

 // Type 'number[]' is not assignable to type 'string[]'. 
 // Type 'number' is not assignable to type 'string'
var arr2: Nullable<Array<string>> = [1,2];
Willem van der Veen
  • 33,665
  • 16
  • 190
  • 155
27
type MyProps = {
  workoutType: string | null;
};
Ritwik
  • 1,597
  • 16
  • 17
12
type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

and then u can use it

Nullable<Employee>

This way you can still use Employee interface as it is somewhere else

Yilmaz
  • 35,338
  • 10
  • 157
  • 202
7

Nullable type can invoke runtime error. So I think it's good to use a compiler option --strictNullChecks and declare number | null as type. also in case of nested function, although input type is null, compiler can not know what it could break, so I recommend use !(exclamination mark).

function broken(name: string | null): string {
  function postfix(epithet: string) {
    return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
  }
  name = name || "Bob";
  return postfix("great");
}

function fixed(name: string | null): string {
  function postfix(epithet: string) {
    return name!.charAt(0) + '.  the ' + epithet; // ok
  }
  name = name || "Bob";
  return postfix("great");
}

Reference. https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-type-assertions

Margaux
  • 139
  • 2
  • 5
  • 1
    Two comments: **a)** Modern JS or TS can use the "null-coalesce" operator `??`. This is less likely to result in accidental errors than the "falsey" logical-OR operator `||`. `name = name ?? "Bob";` is cleaner way to replace `null` with a default value. **b)** I would not use `!` in this code. Any time you use `!`, you make it possible that a future maintainer of code might make a mistake, resulting in a rare runtime error: painful to debug. Safer is `const name2:string = name ?? "Bob";` `function postfix(...) { return name2.charAt(0) ...` – ToolmakerSteve Oct 27 '20 at 00:35
5
type WithNullableFields<T, Fields> = {
  [K in keyof T]: K extends Fields 
    ? T[K] | null | undefined
    : T[K]
}

let employeeWithNullableSalary: WithNullableFields<Employee, "salary"> = {
  id: 1,
  name: "John",
  salary: null
}

Or you can turn off strictNullChecks ;)

And the reversed version:

type WithNonNullableFields<T, Fields> = {
  [K in keyof T]: K extends Fields
    ? NonNullable<T[K]>
    : T[K]
}
Shura
  • 507
  • 5
  • 8
3

I solved this issue by editing the tsconfig.json file.

Under: "strict": true, add those 2 lines:

"noImplicitAny": false,
"strictNullChecks": false,
Tobias S.
  • 21,159
  • 4
  • 27
  • 45
Kayes
  • 55
  • 1
  • 3
  • 8
    does this not defeat the purpose of typescript? surely there has to by a way to set an object property or object to some null safe state? – d0rf47 Nov 27 '21 at 15:27
  • @d0rf47, I haven't tested it but I think you can use the NonNullable helper for that use case https://www.typescriptlang.org/docs/handbook/utility-types.html#nonnullabletype – gxc Mar 02 '22 at 17:59
  • I think this defeats the purpose of everything, haha, just kidding. – windmaomao Oct 12 '22 at 13:36
1

i had this same question a while back.. all types in ts are nullable, because void is a subtype of all types (unlike, for example, scala).

see if this flowchart helps - https://github.com/bcherny/language-types-comparison#typescript

bcherny
  • 3,072
  • 2
  • 27
  • 35
  • 4
    -1: This is not true at all. As for `void` being 'subtype of all types' ([bottom type](https://en.wikipedia.org/wiki/Bottom_type)), refer to [this thread](https://github.com/Microsoft/TypeScript/issues/3076). Also the chart you provided for scala is incorrect as well. `Nothing` in scala is, in fact, the bottom type. Typescript, atm, **does not** have bottom type while scala **does**. – Daniel Shin Jul 25 '16 at 03:00
  • 3
    "Subtype of all types" != bottom type. See the TS spec here https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#327-the-undefined-type – bcherny Jul 26 '16 at 00:06
0

Basing on previous answers, I'd like to add one more solution and explain it.

type Nullable<T> = T | null; 

type SetAllNullable<T, ExceptKeys = void> = {
    [P in keyof T]: P extends ExceptKeys
        ? T[P]
        : Nullable<T[P]>
};

For example we have type Person

type Person = {
  age: number;
  name: string;
  location: string;
}

and we want to set all fields to nullable except name. We can do it this way:

type PersonBrief = SetAllNullable<Person, 'name'>

As a result we'll have a new type where all fields except name can have null as value.

We can also provide a list of fields to be excluded from setting it to nullable. The simple ways is

type PersonBriefOne = SetAllNullable<Person, 'name' | 'age'>;

and a bit more complex is

// `as const` makes this constant read-only and safe for making a type
const excludeFiledsList = ['name', 'age'] as const;

// this tricky string takes all array values by index and creates union type
type ExludedFieldsType = typeof excludeFiledsList[number];

type PersonBriefTwo = SetAllNullable<Person, ExludedFieldsType>;

So types PersonBriefOne and PersonBriefTwo will be similar.

One more note is that ExceptKeys value is optional and can be skipped - in this case all fields will be set to nullable, and even if non-existing key(s) will be provided as second argument, it will not cause any error and all fields will be set to nullable as well.

lazy.lizard
  • 834
  • 6
  • 11