Question

How to Declare Nullable Types in TypeScript Interfaces

typescriptnullable

Question

I have the following TypeScript interface:

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

I want to make the salary field nullable, similar to how nullable types work in C#. Is this possible in TypeScript, and if so, how should it be declared?

Short Answer

By the end of this page, you will understand how nullable types work in TypeScript, how to declare a property such as salary as null, and how this differs from making a property optional. You will also learn common patterns, mistakes, and practical examples for working with null, undefined, and union types in TypeScript.

Concept

In TypeScript, a type is made nullable by using a union type. A union type means a value can be one of several allowed types.

For example:

salary: number | null;

This means salary can hold either:

  • a number
  • or null

That is the TypeScript way to represent nullable values.

This matters because real programs often deal with incomplete or missing data:

  • a salary may not be known yet
  • a database field may contain NULL
  • an API may return null
  • a form field may be intentionally cleared

TypeScript helps you model these cases safely.

A very important detail is the difference between these ideas:

  • nullable: the property exists, but its value may be null
  • optional: the property may be missing entirely

For example:

interface Employee {
  salary: number | null;
}

Here, salary must exist, but it may be null.

interface Employee {
  salary?: number;
}

Here, salary may be omitted completely.

You can also combine both:

interface Employee {
  salary?: number | null;
}

Now salary can be:

  • a number
  • null
  • missing

This distinction is one of the most important parts of modeling data correctly in TypeScript.

Mental Model

Think of a property like a box with a label.

  • salary: number means the box must contain a number.
  • salary: number | null means the box must exist, but it may contain either a number or an explicit "empty" value.
  • salary?: number means the box might not be there at all.

So:

  • null = the box exists, but it is intentionally empty
  • undefined or optional = the box may be absent

This mental model helps when designing interfaces for APIs, database records, and user input.

Take Quiz

Syntax and Examples

The basic syntax for a nullable property in TypeScript is:

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

Example: nullable property

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

const e1: Employee = {
  id: 1,
  name: "Ava",
  salary: 50000
};

const e2: Employee = {
  id: 2,
  name: "Liam",
  salary: null
};

Both objects are valid because salary can be either a number or null.

Example: optional vs nullable

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

const e3: EmployeeOptional = {
  id: 3,
  name: "Noah"
};

This works because salary is optional, so it can be left out entirely.

Example: nullable and optional together

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

This allows all of these:

const a: EmployeeFlexible = { id: 1, name: "Ava", salary: 1000 };
const b: EmployeeFlexible = { id: 2, name: "Liam", salary: null };
const c: EmployeeFlexible = { id: 3, name: "Noah" };

Checking before use

If salary may be null, check it before using it:

function printSalary(employee: Employee): void {
  if (employee.salary !== null) {
    console.log(employee.salary.toFixed(2));
  } else {
    console.log("Salary not available");
  }
}

This is important because TypeScript will not let you safely use a possibly null value without checking it first, especially when strictNullChecks is enabled.

Step by Step Execution

Consider this example:

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

const employee: Employee = {
  id: 1,
  name: "Mia",
  salary: null
};

if (employee.salary !== null) {
  console.log(employee.salary.toFixed(2));
} else {
  console.log("No salary yet");
}

What happens step by step

  1. The interface says salary can be a number or null.
  2. The employee object is created with salary: null.
  3. The if statement checks whether employee.salary is not null.
  4. In this case, the condition is false because the value is null.
  5. The else block runs.
  6. The program prints:
No salary yet

If salary had a number

If you changed it to:

salary: 4200

then:

  1. the condition would be true
  2. toFixed(2) would run safely
  3. the output would be:
4200.00

This is a standard null-check pattern in TypeScript.

Real World Use Cases

Nullable types are common in real applications.

Database records

A column such as salary, middleName, or deletedAt may be NULL in a database.

interface EmployeeRecord {
  id: number;
  salary: number | null;
}

API responses

An API may return null for fields that are known but currently empty.

interface UserProfile {
  bio: string | null;
}

Forms

A user may clear an input, which may be represented as null before saving.

interface FormState {
  age: number | null;
}

Scheduling and timestamps

A task may not have been completed yet.

interface Task {
  completedAt: string | null;
}

Business logic

A value may be intentionally unavailable rather than simply missing.

interface Order {
  discountCode: string | null;
}

Using nullable types makes these cases explicit and easier to reason about.

Take Quiz

Real Codebase Usage

In real projects, developers often use nullable types together with checks, defaults, and validation.

Guard clauses

A common pattern is to return early if a nullable value is missing.

function getAnnualSalary(employee: Employee): number | null {
  if (employee.salary === null) {
    return null;
  }

  return employee.salary * 12;
}

Default values

Sometimes developers convert null to a fallback value.

function getSalaryDisplay(employee: Employee): string {
  return employee.salary ?? 0 + "";
}

A clearer version is:

function getSalaryDisplay(employee: Employee): string {
  return String(employee.salary ?? 0);
}

Validation before saving data

function canSubmit(employee: Employee): boolean {
  return employee.salary !== null;
}

API mapping

Developers often map backend data into frontend models.

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

function toEmployee(data: ApiEmployee): Employee {
  return {
    id: data.id,
    name: data.name,
    salary: data.salary
  };
}

Null-safe formatting

function formatSalary(employee: Employee): string {
  if (employee.salary === null) {
    return "Not set";
  }

  return `$${employee.salary.toFixed(2)}`;
}

In real codebases, the key idea is not just declaring nullable values, but handling them intentionally and safely.

Common Mistakes

1. Confusing nullable with optional

This means nullable:

interface Employee {
  salary: number | null;
}

This means optional:

interface Employee {
  salary?: number;
}

These are not the same.

  • number | null => property must exist, value may be null
  • salary? => property may be missing

2. Forgetting to check for null before using the value

Broken code:

interface Employee {
  salary: number | null;
}

function printSalary(employee: Employee) {
  console.log(employee.salary.toFixed(2));
}

If salary is null, this fails.

Correct:

function printSalary(employee: Employee) {
  if (employee.salary !== null) {
    console.log(employee.salary.toFixed(2));
  }
}

3. Assuming null and undefined are the same

They are related, but not identical.

let a: number | null = null;
let b: number | undefined = undefined;

Use the one that matches your data model.

4. Using optional when the field should always exist

Broken design:

interface Employee {
  salary?: number;
}

If every employee record should always contain the salary key, even when empty, use:

interface Employee {
  salary: number | null;
}

5. Forgetting strictNullChecks

If strictNullChecks is disabled, TypeScript is less strict about null and undefined. That can hide problems.

In modern TypeScript projects, enabling strict mode is recommended.

{
  "compilerOptions": {
    "strict": true
  }
}

Comparisons

PatternMeaningExampleWhen to use
salary: numberMust be a numbersalary: 5000When the value is always required
`salary: numbernull`Must exist, but may be nullsalary: null
salary?: numberMay be missingno salary keyWhen the property is optional
`salary?: numbernull`May be missing or nullsalary: null or omitted
`salary: numberundefined`Must exist, but may be undefinedsalary: undefined

null vs undefined

ValueMeaning
nullIntentionally empty value
undefinedNot provided or not yet assigned

C# vs TypeScript style

LanguageNullable style
C#int?
TypeScript`number

TypeScript does support nullable behavior, but it uses union types instead of special ? nullable syntax on primitive types.

Take Quiz

Cheat Sheet

// Nullable property
interface Employee {
  salary: number | null;
}

// Optional property
interface Employee {
  salary?: number;
}

// Optional + nullable
interface Employee {
  salary?: number | null;
}

Rules

  • Use | null when a property may explicitly hold null
  • Use ? when a property may be omitted
  • Use both if the property may be omitted or null
  • Check nullable values before calling methods on them
  • Prefer strictNullChecks or strict: true

Common checks

if (employee.salary !== null) {
  console.log(employee.salary);
}
const value = employee.salary ?? 0;

Quick decision guide

  • Always present, always number -> number
  • Always present, maybe empty -> number | null
  • Sometimes missing -> salary?: number
  • Missing or explicitly empty -> salary?: number | null

Important edge case

salary?: number does not mean the same thing as salary: number | null.

FAQ

How do I make a field nullable in TypeScript?

Use a union type:

salary: number | null;

Is salary?: number the same as salary: number | null?

No. salary?: number means the property can be missing. salary: number | null means the property must exist, but its value can be null.

Can a TypeScript property be both optional and nullable?

Yes:

salary?: number | null;

What is the TypeScript equivalent of C# int??

The closest equivalent is:

number | null

Should I use null or undefined in TypeScript?

Use whichever best matches your data model. null usually means intentionally empty. undefined usually means missing or not assigned.

Why does TypeScript ask me to check for null first?

Because a value like number | null is not guaranteed to be a number. TypeScript wants you to handle the null case safely.

Does this depend on strictNullChecks?

Yes. Nullable behavior is much safer and clearer when strictNullChecks or full strict mode is enabled.

Take Quiz

Mini Project

Description

Build a small employee formatter that safely handles nullable salary values. This demonstrates how to declare nullable properties in an interface and how to work with them without runtime errors.

Goal

Create a TypeScript program that prints employee salary information, showing a fallback message when salary is null.

Requirements

[ "Create an Employee interface with a nullable salary field", "Create at least two employee objects, one with a number salary and one with null", "Write a function that returns a readable salary message", "Avoid calling number methods unless salary is confirmed to be a number" ]

Take Quiz

Keep learning

Related questions

View more