TypeScript and its eternal question: how to make a type nullable?
\n\nHello, colleague! If you've ever written in TypeScript, you've surely encountered a situation where a function might return a value, or might not. Or a variable is not initialized immediately. And then the pivotal question arises: how to declare a type that can be null or undefined?
This question is one of the most popular on Stack Overflow (tens of thousands of views!), and for good reason. TypeScript is strict, but fair. It doesn't allow us to just assign null to a variable of type string. And that's great because it saves us from a ton of bugs. But what to do when nullable is part of the business logic? Let's figure it out.
In this article, we won't just give a dry answer. We'll go from the basics to advanced techniques, analyze common mistakes, and learn how to write code that is safe and elegant. Let's go!
\n\nBasics: Union Types and Null
\n\nThe simplest and most direct way to make a type nullable is to use a union type. If a variable can be a string or null, we write it like this:
\n\nlet userName: string | null = null;\nuserName = "Alice"; // Ok\nuserName = null; // Also ok\n\n\nThe same works with undefined. In TypeScript, null and undefined are different types (unless the --strictNullChecks flag is enabled, but more on that later).
let age: number | undefined;\nage = 25;\nage = undefined; // Works\n\n\nImportant: if you use | null or | undefined, TypeScript will force you to check the value before using it. Attempting to call a method on a potentially null object will result in a compilation error. This is the very "safety" we love TS for.
Real-life example: finding a user
\n\ninterface User {\n id: number;\n name: string;\n}\n\nfunction findUser(id: number): User | null {\n // ...search logic\n if (id === 1) {\n return { id: 1, name: "Ivan" };\n }\n return null;\n}\n\nconst user = findUser(2);\n// user.name; // Error! 'user' is possibly null\nif (user) {\n console.log(user.name); // Now it's safe\n}\n\n\nSee? The compiler itself hints to you: "Hey, check it, or you'll crash!". This saves hours of debugging.
\n\nOptional Parameters and Properties (Optional Chaining)
\n\nOften, not only variables but also object fields or function arguments can be nullable. For this, TypeScript has a special syntax — the optional modifier (question mark ?).
Optional function parameters
\n\nfunction greet(name?: string) {\n if (name) {\n return `Hello, ${name}!`;\n }\n return "Hello, stranger!";\n}\n\nconsole.log(greet("Peter")); // Hello, Peter!\nconsole.log(greet()); // Hello, stranger!\n\n\nThe type of the name parameter automatically becomes string | undefined. Note: if a parameter is optional, you can omit it, and TS won't complain.
Optional interface properties
\n\ninterface Config {\n url: string;\n port?: number; // can be number or undefined\n}\n\nconst config1: Config = { url: "http://localhost" }; // port is missing\nconst config2: Config = { url: "http://localhost", port: 3000 };\n\n\nWhen accessing config1.port, TypeScript will say it's number | undefined. And that's correct — we don't know if the port is set.
Optional Chaining — your best friend
\n\nWith the advent of ES2020 and TypeScript 3.7, we have an elegant way to safely access nested properties that might be null or undefined. This is called optional chaining
Blogs
Book Recommendations
Read also