This TypeScript error occurs when you try to pass a value that might be undefined to a function parameter that doesn't explicitly accept undefined in its type. The fix involves either adding undefined to the parameter type union, using optional parameters, or checking the value before passing it.
The error "Argument of type 'undefined' is not assignable to parameter of type 'string | undefined'" occurs when TypeScript's strict null checking detects a type mismatch. This typically happens when: 1. You're passing a variable that might be undefined to a function parameter 2. The parameter's type annotation doesn't include undefined in its union type 3. TypeScript cannot prove that the variable is defined when you call the function When strictNullChecks is enabled (recommended), TypeScript treats undefined and null as distinct types that must be explicitly included in type unions. This prevents runtime errors where you accidentally try to use undefined values as if they were the expected type. The error is TypeScript's way of protecting you from undefined-related runtime bugs. Unlike JavaScript, which silently coerces undefined in many contexts, TypeScript forces you to handle these cases explicitly.
The most common fix is to explicitly add undefined to the parameter's type annotation if the function is designed to accept undefined:
Before (causes error):
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user = getName(); // returns string | undefined
greet(user); // ERROR: undefined not assignable to stringAfter (fixed):
function greet(name: string | undefined) {
console.log(`Hello, ${name}`);
}
const user = getName(); // returns string | undefined
greet(user); // OK - function accepts undefinedUsing optional parameters (shorthand):
// These are equivalent:
function greet(name?: string) { }
function greet(name: string | undefined) { }
// Both allow calling with undefined or omitting the argument
greet(undefined); // OK
greet(); // OK
greet("Alice"); // OKThis is the preferred approach when the function logic can handle undefined values.
If you can't change the function signature, check that the variable is defined before passing it:
Using if statement:
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user = getName(); // returns string | undefined
if (user !== undefined) {
greet(user); // OK - TypeScript knows user is string here
}Using if with truthy check (for non-empty strings):
const user = getName();
if (user) { // Also checks for empty strings
greet(user); // OK
}Using logical AND operator:
const user = getName();
user && greet(user); // Executes greet only if user is truthyUsing optional chaining with nullish coalescing:
const user = getName() ?? "Guest";
greet(user); // OK - user is guaranteed to be stringType guards help TypeScript narrow the type, proving the value is safe to use.
If you're certain a value will be defined at runtime, use the ! operator:
Using non-null assertion:
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user = getName(); // returns string | undefined
greet(user!); // OK - ! tells TypeScript to trust youImportant: This is a last resort. Only use when you're absolutely certain the variable will be defined:
// Good use case - you know the DOM element exists
const button = document.getElementById("submit")!;
button.addEventListener("click", () => { });
// Bad use case - you're not sure the value exists
const user = getUserFromAPI();
greet(user!); // May crash at runtime if user is undefinedWarning: The ! operator bypasses TypeScript's safety checks. If you're wrong about the value being defined, your code will crash at runtime. Prefer type guards when possible.
The nullish coalescing operator (??) provides a fallback value when the original is null or undefined:
Basic example:
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user = getName(); // returns string | undefined
greet(user ?? "Guest"); // OK - user is string | undefined, ?? returns stringWith object properties:
interface User {
name?: string;
}
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user: User = getUser();
greet(user.name ?? "Anonymous"); // OKDifference from logical OR (||):
// || replaces falsy values (0, empty string, false, null, undefined)
const count = getCachedCount() || 0; // "0" becomes 0
// ?? only replaces null/undefined
const count = getCachedCount() ?? 0; // "0" stays "0"Nullish coalescing is ideal when you want a sensible default and can't change the function.
If strictNullChecks is disabled, enable it to get better type safety:
Check your tsconfig.json:
{
"compilerOptions": {
"strict": true,
// or explicitly:
"strictNullChecks": true
}
}If you have "strict": true, strictNullChecks is already enabled.
If strictNullChecks is false and you're seeing this error:
It's likely enabled in a different config file, or you're using a build tool that enforces it. Search for other tsconfig files in your project:
find . -name "tsconfig*.json" -type fWhy strict null checks are essential:
- Catches undefined/null errors at compile time, not runtime
- Prevents silent coercions that hide bugs
- Makes your code more explicit and maintainable
- Industry best practice (Next.js, React, Angular all enable it)
Enabling strict checks incrementally:
If you have a large codebase with strictNullChecks disabled, enable it gradually:
1. Set "strictNullChecks": true in tsconfig.json
2. Fix compilation errors in one module at a time
3. Don't disable the flag to ignore errors - address them properly
4. Once all errors are fixed, enjoy the safety benefits
For complex scenarios, define assertion functions that tell TypeScript a value is safe:
Basic assertion function:
function assertDefined<T>(value: T): asserts value is NonNullable<T> {
if (value === null || value === undefined) {
throw new Error("Value is null or undefined");
}
}
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user = getName(); // string | undefined
assertDefined(user);
greet(user); // OK - TypeScript knows user is string nowAssertion function with custom check:
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new Error(`Expected string, got ${typeof value}`);
}
}
const user: unknown = getUserInput();
assertIsString(user);
greet(user); // OK - user is stringUsing a validation library:
import { z } from "zod";
const UserSchema = z.object({
name: z.string(),
});
const data = getDataFromAPI(); // unknown
const user = UserSchema.parse(data); // throws if invalid
greet(user.name); // OK - user.name is guaranteed stringAssertion functions are powerful for runtime validation and type narrowing.
When accessing optional properties, handle undefined values explicitly:
Problem with optional properties:
interface User {
name?: string;
}
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user: User = getUser();
greet(user.name); // ERROR - name might be undefinedSolution 1: Use optional chaining with nullish coalescing:
const user: User = getUser();
greet(user?.name ?? "Guest"); // OKSolution 2: Destructure with defaults:
const { name = "Guest" } = getUser();
greet(name); // OK - name is guaranteed stringSolution 3: Use optional parameter in function:
function greet(name?: string) {
console.log(`Hello, ${name ?? "Guest"}`);
}
const user: User = getUser();
greet(user.name); // OK - function accepts undefinedSolution 4: Update interface if property should always exist:
interface User {
name: string; // Remove ? - name is required
}
const user: User = getUser();
greet(user.name); // OK - guaranteed to existChoose the solution based on whether the property should truly be optional.
strictNullChecks: The Foundation of TypeScript Safety
When strictNullChecks is enabled, null and undefined stop being assignable to all types. This is the correct default behavior and aligns with JavaScript semantics where accessing undefined is a runtime error.
Why this is important:
In JavaScript, many operations return undefined implicitly (missing return statements, undefined object properties, missing array elements). TypeScript's strict null checks force you to handle these cases explicitly, catching bugs before they reach production.
Type Narrowing Patterns:
TypeScript understands several patterns that narrow types:
1. Truthiness checks:
const value: string | undefined = getValue();
if (value) {
// value is string here (not empty)
}2. Explicit undefined checks:
if (value !== undefined) {
// value is string here
}3. Logical operators:
const safe = value && procesString(value); // safe only if value is truthy
const withDefault = value || "default"; // string4. Optional chaining:
const length = value?.length; // undefined if value is undefined5. Nullish coalescing:
const result = value ?? fallback; // Uses fallback only for null/undefinedUnion Types in Function Parameters:
When a function parameter has a union type, it can accept any type in the union:
function process(value: string | number | undefined) {
// Can be any of the three types
}
process("hello"); // OK
process(42); // OK
process(undefined); // OK
process(null); // ERROR - null not in unionCommon Patterns:
1. Optional parameters:
function configure(debug?: boolean) { } // boolean | undefined
configure(); // OK
configure(true); // OK
configure(undefined); // OK2. Null or undefined (classic):
function setup(config: Config | null | undefined) { }3. Required with default in caller:
function greet(name: string) { } // Required
const user = getUserOrDefault(); // Returns string (never undefined)
greet(user); // OKInteroperability with Untyped Code:
When working with JavaScript libraries or untyped code, you may need to assert types:
// From untyped library
const result: any = externalLibrary.getData();
// Option 1: Type assertion
const data = result as string;
// Option 2: Validate first
if (typeof result === "string") {
process(result);
}
// Option 3: Use a schema library
const data = parseWith(MySchema, result);Performance Considerations:
Strict null checking has no runtime performance impact - it's purely a compile-time feature. Type narrowing is also compile-time only; the generated JavaScript doesn't reflect narrowing.
Migration to Strict Null Checks:
If adding this to a legacy codebase:
1. Set "strictNullChecks": true in tsconfig.json
2. Run tsc to see all violations
3. Fix errors systematically (don't disable the flag)
4. Use assertion functions and guards to narrow types
5. Update function signatures to be explicit about nullable parameters
6. Add tests for null/undefined cases
Related TypeScript Errors:
- TS2532: Object is possibly undefined - accessing properties
- TS2531: Object is possibly null or undefined - similar to TS2345
- TS2349: Cannot invoke optional method - calling functions that might be undefined
- TS7053: Element implicitly has unknown type - accessing with potentially undefined keys
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript