Amitabh
Amitabh

Reputation: 61167

How to declare a type as nullable in TypeScript?

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?

Upvotes: 517

Views: 829340

Answers (14)

Dorian Andrés
Dorian Andrés

Reputation: 221

You can define a Nullable like type like the following:

type NullOrUndefinedOr<T> = T extends void ? never : null | undefined | T;

This is an example of how it would look:

/* "fictitious" src/services/product.ts */

import { Product } from "@/models/product";
import { ProductRepository } from "@/repositories/product";
import { Logger } from "@/telemetry";

const log = Logger.create(__filename);

export async function getProductById(id: int): Promise<NullOrUndefinedOr<Product>> {
  try{
    const repo = await ProductRepository.create(process.env.DB_CONN_STRING);
    return await repo.findOne({ productId: id });
  }catch(err){
    log.exception(err);
    return null;
  }
}

Note: You can also add this type to your src/types/global.d.ts to used it anywhere in your project without having to import it.

/* global.d.ts */

declare global {
  type NullOrUndefinedOr<T> = T extends void ? never : null | undefined | T;
  
  /** define all global types you need **/
}

/**
if you don't import or export anything else in this file
ensure to export at least an empty object to avoid the
compiler skipping this file
**/
export {};

Upvotes: 0

alvaro.canepa
alvaro.canepa

Reputation: 592

Can define property as Partial and Nullable with type:

export type PartialNullable<T> = {
  [P in keyof T]?: T[P] | null;
};

Them just use

interface Employee {
  id: number;
  company_id: number;
  name: string;
  age: number;
};

const EmployeeData: PartialNullable<Employee> = {
    age: null,
    name: null
};

Upvotes: 2

lazy.lizard
lazy.lizard

Reputation: 919

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.

Upvotes: 2

Shura
Shura

Reputation: 667

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]
}

Upvotes: 11

Yilmaz
Yilmaz

Reputation: 49182

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

Upvotes: 26

Kayes
Kayes

Reputation: 65

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

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

"noImplicitAny": false,
"strictNullChecks": false,

Upvotes: 4

Tim Santeford
Tim Santeford

Reputation: 28111

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

Upvotes: 360

Willem van der Veen
Willem van der Veen

Reputation: 36580

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];

Upvotes: 43

Margaux
Margaux

Reputation: 149

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

Upvotes: 8

Ritwik
Ritwik

Reputation: 1725

type MyProps = {
  workoutType: string | null;
};

Upvotes: 29

bjaksic
bjaksic

Reputation: 3313

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.

Upvotes: 276

bcherny
bcherny

Reputation: 3172

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

Upvotes: 1

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 220884

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';
}

Upvotes: 438

Miguel Ventura
Miguel Ventura

Reputation: 10458

Just add a question mark ? to the optional field.

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

Upvotes: 75

Related Questions