This error occurs when you pass a value that might be undefined to a function that expects a non-nullable string. The fix involves using type guards, optional chaining, nullish coalescing, or adjusting your function signatures to accept optional parameters.
TypeScript's type system enforces type safety at compile time. When `strictNullChecks` is enabled (recommended in modern TypeScript), `undefined` is treated as its own distinct type and cannot be assigned to a type like `string`. This error typically appears when you: - Pass a potentially undefined value (from optional parameters, API responses, or object properties) to a function expecting a string - Use environment variables or object properties that might not exist - Call functions with optional parameters without checking if they're defined The error message tells you exactly what's wrong: you're attempting to pass a value that TypeScript has inferred as `undefined | string` (or just `undefined`) to a parameter that only accepts `string`.
First, verify that strictNullChecks is enabled in your tsconfig.json:
{
"compilerOptions": {
"strictNullChecks": true
}
}If it's not enabled, you can either:
- Enable it (recommended for new projects)
- Or disable it temporarily to find the issue, then fix it properly
For existing projects, gradually enabling strict mode is the best approach.
The safest approach is to check if the value is defined before passing it:
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const userInput: string | undefined = getUserInput();
// Check if it's defined
if (userInput !== undefined) {
greet(userInput); // TypeScript knows it's safe now
} else {
console.log("Name is required");
}This type narrowing tells TypeScript the value is definitely a string within the if block.
Provide a fallback value using ??:
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const userInput: string | undefined = getUserInput();
// Provide a default value
greet(userInput ?? "Guest");This works great for optional function parameters where you have a sensible default.
When accessing nested properties that might not exist, use optional chaining (?.):
function processUser(user: { name?: string }) {
const name = user?.name ?? "Unknown";
console.log(name);
}
// Both of these work now
processUser({ name: "Alice" });
processUser({}); // Falls back to "Unknown"Optional chaining returns undefined if the property doesn't exist, which you can then handle with ??.
If the function should accept undefined values, modify its signature:
// Before: requires a string
function greet(name: string) {
console.log(`Hello, ${name}`);
}
// After: accepts string or undefined
function greet(name: string | undefined) {
console.log(`Hello, ${name ?? "Guest"}`);
}
// Or use optional parameter syntax
function greet(name?: string) {
console.log(`Hello, ${name ?? "Guest"}`);
}The second form with name?: string is cleaner and more readable.
Environment variables are always string | undefined. Type them explicitly:
function connectToDb(url: string) {
// connect to url
}
// Problem
connectToDb(process.env.DATABASE_URL); // TS2345 error
// Solution 1: Check it exists
const dbUrl = process.env.DATABASE_URL;
if (!dbUrl) throw new Error("DATABASE_URL is required");
connectToDb(dbUrl);
// Solution 2: Provide a default
connectToDb(process.env.DATABASE_URL ?? "localhost:5432");
// Solution 3: Use a validation library like zod
import { z } from "zod";
const config = z.object({
DATABASE_URL: z.string(),
}).parse(process.env);
connectToDb(config.DATABASE_URL);If you're absolutely certain a value is defined, you can use the non-null assertion operator:
const name: string | undefined = getUserName();
// Tell TypeScript to trust you - use !
function greet(name: string) {
console.log(`Hello, ${name}`);
}
greet(name!); // ! asserts that name is definitely a stringWarning: Only use this when you're 100% sure. If the value is actually undefined at runtime, you'll get undefined behavior instead of a compile-time error.
### strictNullChecks vs. Non-strict Mode
With strictNullChecks: false, TypeScript allows undefined to be assigned to any type (permissive but less safe). With it enabled, you must explicitly handle null and undefined.
### Type Narrowing Techniques
TypeScript narrows types automatically when you use certain checks:
let value: string | undefined;
// typeof check
if (typeof value === "string") {
// Inside this block, value is string (not undefined)
}
// Truthiness check (but watch out for empty strings!)
if (value) {
// value is truthy here (not undefined, not empty string)
}
// Strict equality check
if (value !== undefined && value !== null) {
// value is definitely a string now
}
// Optional chaining in conditions
if (value?.length > 0) {
// If value is undefined, this short-circuits
}### Common Patterns
React component props:
interface Props {
name?: string; // Optional prop
}
function MyComponent({ name = "Guest" }: Props) {
return <p>Hello, {name}</p>;
}API responses:
const data = await fetch("/api/user").then(r => r.json());
const username = data?.user?.name ?? "Anonymous";### When to Use Union Types
If a function genuinely needs to handle both string and undefined:
function greet(name: string | undefined) {
if (name) {
console.log(`Hello, ${name}`);
} else {
console.log("Hello, anonymous");
}
}This is clearer than using optional parameters when the distinction matters.
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