Fix: TypeScript Type 'X | undefined' is not assignable to type 'X'
Quick Answer
How to fix TypeScript strict null checks error Type X undefined is not assignable caused by optional values, nullable types, missing guards, and strictNullChecks.
The Error
You compile TypeScript and get:
error TS2322: Type 'string | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.Or variations:
error TS2532: Object is possibly 'undefined'.error TS2322: Type 'number | null' is not assignable to type 'number'.
Type 'null' is not assignable to type 'number'.error TS18048: 'user' is possibly 'undefined'.error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.TypeScript’s strict null checking is telling you that a value might be undefined or null, but the target type does not accept those values. You need to narrow the type before using it.
Why This Happens
When strictNullChecks is enabled in tsconfig.json (or strict: true, which includes it), TypeScript tracks null and undefined as distinct types. A string type only accepts strings — not undefined or null.
Operations that return possibly-undefined values:
// Optional properties are T | undefined
interface User {
name: string;
email?: string; // string | undefined
}
// Array.find() returns T | undefined
const user = users.find(u => u.id === 1); // User | undefined
// Map.get() returns T | undefined
const value = myMap.get("key"); // V | undefined
// Optional chaining returns T | undefined
const city = user?.address?.city; // string | undefined
// Object property access with index signature
const item = record["key"]; // T | undefinedCommon causes:
- Optional object properties. Properties marked with
?areT | undefined. - Array methods.
find,pop,shiftcan returnundefined. - Map and Set lookups.
.get()returnsT | undefined. - Function parameters. Optional parameters are
T | undefined. - DOM methods.
document.getElementById()returnsHTMLElement | null. - External API data. API responses may have nullable fields.
Fix 1: Use Type Guards (if/else)
The most reliable approach. Check for undefined/null before using the value:
Broken:
function greet(user: User) {
const email: string = user.email; // Error: string | undefined not assignable to string
sendEmail(email);
}Fixed — check with if statement:
function greet(user: User) {
if (user.email !== undefined) {
// TypeScript knows email is string here (narrowed)
sendEmail(user.email);
}
}For null checks:
const element = document.getElementById("app");
if (element !== null) {
element.textContent = "Hello"; // TypeScript knows element is HTMLElement
}Combining null and undefined checks:
if (value != null) {
// Checks both null and undefined (loose equality)
process(value); // TypeScript narrows correctly
}Pro Tip: Use
!= null(loose equality with two=) to check for bothnullandundefinedin one condition. This is one of the rare cases where loose equality is preferred over strict equality in TypeScript.
Fix 2: Use the Nullish Coalescing Operator (??)
Provide a default value when the value might be null or undefined:
interface Config {
timeout?: number;
retries?: number;
baseUrl?: string;
}
function createClient(config: Config) {
const timeout: number = config.timeout ?? 5000;
const retries: number = config.retries ?? 3;
const baseUrl: string = config.baseUrl ?? "https://api.example.com";
}?? vs ||:
// ?? only falls back for null/undefined
const count = config.count ?? 10;
// If config.count is 0, count is 0 (correct!)
// If config.count is undefined, count is 10
// || falls back for ANY falsy value (0, "", false, null, undefined)
const count = config.count || 10;
// If config.count is 0, count is 10 (probably wrong!)Use ?? when 0, "", or false are valid values.
Fix 3: Use Non-Null Assertion (!)
When you are certain the value is not null/undefined:
// You know the element exists because your HTML has it
const app = document.getElementById("app")!;
app.textContent = "Hello";
// After a check in a different scope
const user = users.find(u => u.id === id);
// You know the user exists because of business logic
processUser(user!);Warning: The ! operator tells TypeScript to trust you. If the value actually is null/undefined at runtime, your code will crash. Only use it when you are genuinely certain.
Better alternatives when possible:
// Instead of:
const element = document.getElementById("app")!;
// Use a guard with an error:
const element = document.getElementById("app");
if (!element) throw new Error("App element not found");
// element is now HTMLElement (narrowed)Common Mistake: Overusing the non-null assertion operator (
!) to silence TypeScript errors. Every!is a place where your code can crash at runtime. Use it sparingly and only when you can guarantee the value exists.
Fix 4: Use Optional Chaining (?.)
Access deeply nested optional properties safely:
interface Order {
customer?: {
address?: {
city?: string;
};
};
}
// Broken — any level could be undefined
const city: string = order.customer.address.city; // Multiple errors!
// Fixed — optional chaining
const city: string | undefined = order.customer?.address?.city;
// With a default value
const city: string = order.customer?.address?.city ?? "Unknown";With method calls:
const length: number = user.getName?.().length ?? 0;With array access:
const firstItem: string | undefined = items?.[0];Fix 5: Fix Array Methods
find, pop, shift, and index access return possibly-undefined:
Broken:
const users: User[] = getUsers();
const admin: User = users.find(u => u.role === "admin");
// Error: User | undefined is not assignable to UserFixed — type guard:
const admin = users.find(u => u.role === "admin");
if (admin) {
processAdmin(admin); // admin is User here
}Fixed — with assertion and error:
const admin = users.find(u => u.role === "admin");
if (!admin) {
throw new Error("No admin user found");
}
processAdmin(admin); // admin is User hereFixed — filter and assert the type:
// filter with type predicate
const admins: User[] = users.filter((u): u is User => u.role === "admin");
const firstAdmin: User | undefined = admins[0];For array index access:
const items: string[] = ["a", "b", "c"];
// With noUncheckedIndexedAccess (tsconfig), items[0] is string | undefined
const first = items[0]; // string | undefined
// Fixed
if (items.length > 0) {
const first = items[0]!; // Safe because we checked length
}Fix 6: Fix Function Parameter Types
Optional parameters and return types:
// Optional parameter
function greet(name?: string) {
// name is string | undefined
const upper: string = name.toUpperCase(); // Error!
// Fixed
const upper: string = (name ?? "World").toUpperCase();
}
// Return type might be undefined
function findUser(id: number): User | undefined {
return users.find(u => u.id === id);
}
// Caller must handle undefined
const user = findUser(1);
if (user) {
console.log(user.name);
}Overloads for different return types:
function findUser(id: number, required: true): User;
function findUser(id: number, required?: false): User | undefined;
function findUser(id: number, required = false): User | undefined {
const user = users.find(u => u.id === id);
if (required && !user) throw new Error(`User ${id} not found`);
return user;
}
const user = findUser(1, true); // Type is User (guaranteed)
const maybe = findUser(1); // Type is User | undefinedFix 7: Fix Map and Record Types
Map.get() and record indexing return possibly-undefined:
const cache = new Map<string, User>();
// Map.get returns V | undefined
const user: User = cache.get("alice"); // Error!
// Fixed — check with has() or guard
if (cache.has("alice")) {
const user = cache.get("alice")!; // Safe after has() check
}
// Fixed — guard
const user = cache.get("alice");
if (user) {
process(user);
}Record with index signature:
const config: Record<string, string> = loadConfig();
const value: string = config["key"]; // string (Record assumes keys exist)
// BUT with noUncheckedIndexedAccess: string | undefined
// Safer approach
const value = config["key"];
if (value !== undefined) {
use(value);
}Fix 8: Create Type-Safe Helper Functions
Build reusable utilities for common patterns:
// Assert non-null with a descriptive error
function assertDefined<T>(value: T | undefined | null, message: string): T {
if (value === undefined || value === null) {
throw new Error(message);
}
return value;
}
const user = assertDefined(
users.find(u => u.id === id),
`User with id ${id} not found`
);
// user is User (guaranteed)
// Type-safe Map wrapper
class TypedMap<K, V> {
private map = new Map<K, V>();
get(key: K): V | undefined {
return this.map.get(key);
}
getOrThrow(key: K): V {
const value = this.map.get(key);
if (value === undefined) throw new Error(`Key not found: ${String(key)}`);
return value;
}
getOrDefault(key: K, defaultValue: V): V {
return this.map.get(key) ?? defaultValue;
}
}Still Not Working?
Check your tsconfig.json settings:
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true
}
}If strictNullChecks is false, these errors will not appear — but you lose important safety checks. Keeping it enabled is recommended.
Use Partial<T> for objects with all optional properties:
type Config = Partial<FullConfig>;
// All properties become optional (T | undefined)Use Required<T> to make all properties required:
type StrictConfig = Required<Config>;
// All properties become required (no undefined)Use NonNullable<T> to strip null/undefined:
type MaybeString = string | null | undefined;
type DefiniteString = NonNullable<MaybeString>; // stringFor TypeScript property errors, see Fix: TypeScript Property does not exist on type. For module resolution errors, see Fix: TypeScript Cannot find module. For general type assignment errors, see Fix: TypeScript type is not assignable.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: TypeError: x is not a function
How to fix JavaScript TypeError is not a function caused by wrong variable types, missing imports, overwritten variables, incorrect method names, and callback issues.
Fix: TypeScript Argument of type 'X' is not assignable to parameter of type 'Y' (TS2345)
How to fix TypeScript error TS2345 Argument of type is not assignable to parameter of type, covering null narrowing, union types, generics, callback types, type widening, enums, and React event handlers.
Fix: TypeScript Could not find a declaration file for module (TS7016)
How to fix the TypeScript TS7016 error 'Could not find a declaration file for module' by installing @types packages, creating declaration files, and configuring tsconfig.json.
Fix: TypeScript Property does not exist on type (TS2339)
How to fix TypeScript error TS2339 'Property does not exist on type'. Covers missing interface properties, type narrowing, optional chaining, intersection types, index signatures, type assertions, type guards, window augmentation, and discriminated unions.