This TypeScript error occurs when you explicitly declare a return type for a function but the function doesn't actually return a value in all code paths. The fix involves either adding return statements to satisfy the declared type or changing the return type to void.
TypeScript enforces that when you explicitly specify a non-void return type for a function, all execution paths through that function must return a value matching that type. Error TS7030 is emitted when the compiler detects at least one code path that could exit the function without returning anything. This error is distinct from implicit type inference issuesโit specifically occurs when you've explicitly declared a return type (like `number`, `string`, `Promise<User>`, etc.) but your implementation doesn't guarantee a return value. The error commonly appears in three scenarios: 1. **Missing return statement**: The function body never calls `return` 2. **Conditional returns**: Some branches return a value while others don't 3. **Early returns without fallback**: The function returns early in some cases but lacks a final return statement TypeScript's control flow analysis examines all possible execution paths, including if/else branches, switch cases, try/catch blocks, and loops. If any path could reach the end of the function without returning, the compiler raises this error.
First, check if your function actually needs to return a value. If the function only performs side effects (logging, updating state, etc.), it shouldn't have a non-void return type.
// WRONG - declares number but doesn't return
function logValue(x: number): number {
console.log(x);
// Missing return statement
}
// CORRECT - if function only logs, use void
function logValue(x: number): void {
console.log(x);
// No return needed for void
}
// CORRECT - if function should return, add return statement
function doubleValue(x: number): number {
console.log(x);
return x * 2;
}If the return type was added automatically by your IDE or a linter, verify it matches your intention.
If your function should return a value but doesn't have a return statement, add one:
// BEFORE: Error - no return statement
function calculateTotal(price: number, tax: number): number {
const total = price + (price * tax);
// Missing: return total;
}
// AFTER: Add return statement
function calculateTotal(price: number, tax: number): number {
const total = price + (price * tax);
return total;
}
// Or more concisely
function calculateTotal(price: number, tax: number): number {
return price + (price * tax);
}For arrow functions:
// BEFORE: Error - explicit return type but no return
const add = (a: number, b: number): number => {
a + b; // This doesn't return!
};
// AFTER: Add explicit return
const add = (a: number, b: number): number => {
return a + b;
};
// Or use implicit return (no braces)
const add = (a: number, b: number): number => a + b;Check that every if/else branch and switch case returns a value:
// BEFORE: Error - else branch missing return
function getStatus(code: number): string {
if (code === 200) {
return "OK";
} else if (code === 404) {
return "Not Found";
}
// Missing: what if code is neither 200 nor 404?
}
// AFTER: Add return for all paths
function getStatus(code: number): string {
if (code === 200) {
return "OK";
} else if (code === 404) {
return "Not Found";
}
return "Unknown"; // Default case
}
// Or use a switch with default
function getStatus(code: number): string {
switch (code) {
case 200:
return "OK";
case 404:
return "Not Found";
default:
return "Unknown";
}
}For ternary expressions:
// BEFORE
function getMessage(success: boolean): string {
return success ? "Success" : undefined; // undefined doesn't match string
}
// AFTER
function getMessage(success: boolean): string {
return success ? "Success" : "Failed";
}For async functions returning Promise<T>, ensure you actually return the promise or await the value:
// BEFORE: Error - async function must return Promise<User>
async function getUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
// Missing: return user;
}
// AFTER: Add return statement
async function getUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const user = await response.json();
return user;
}
// Or directly return the promise
async function getUser(id: string): Promise<User> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}For functions returning promises without async:
// BEFORE: Must return Promise<number>
function delayedCalculation(): Promise<number> {
setTimeout(() => {
return 42; // This returns from the callback, not the function!
}, 1000);
}
// AFTER: Wrap in a promise
function delayedCalculation(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(42);
}, 1000);
});
}If your function doesn't need to return anything, explicitly mark it as void:
// BEFORE: Incorrectly typed as string
function displayMessage(msg: string): string {
console.log(msg);
// No return statement - this is a side effect only
}
// AFTER: Change to void
function displayMessage(msg: string): void {
console.log(msg);
}
// For event handlers in React/DOM
const handleClick = (e: MouseEvent): void => {
console.log("Clicked!");
e.preventDefault();
// No return needed
};For functions that don't return but might throw:
// TypeScript doesn't recognize throw as terminal
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}
// Use 'never' return type for functions that always throw or exit
function exitWithError(msg: string): never {
console.error(msg);
process.exit(1);
}For validation functions that throw or exit, use assertion signatures:
// BEFORE: TypeScript doesn't know this throws
function validateUser(user: unknown): User {
if (!user || typeof user !== "object") {
throw new Error("Invalid user");
}
// TypeScript still expects a return here
}
// AFTER: Use assertion signature (asserts)
function validateUser(user: unknown): asserts user is User {
if (!user || typeof user !== "object") {
throw new Error("Invalid user");
}
// No return needed - this is an assertion function
}
// Or return a type guard
function isUser(user: unknown): user is User {
return user !== null &&
typeof user === "object" &&
"id" in user;
}For exhaustiveness checking:
type Status = "pending" | "success" | "error";
function handleStatus(status: Status): string {
switch (status) {
case "pending":
return "Loading...";
case "success":
return "Done!";
case "error":
return "Failed";
default:
// Ensures all cases are handled
const _exhaustive: never = status;
return _exhaustive;
}
}### The noImplicitReturns Compiler Option
Enable noImplicitReturns in your tsconfig.json to catch missing returns across your entire codebase:
{
"compilerOptions": {
"noImplicitReturns": true,
"strict": true
}
}This option makes TypeScript verify that all code paths in functions with non-void return types explicitly return a value. It's stricter than the default behavior and catches subtle bugs.
### Type Inference vs Explicit Types
TypeScript infers return types based on what you actually return:
// Inferred as (x: number) => number (no error)
function double(x: number) {
return x * 2;
}
// Inferred as (x: number) => void (no error even without return)
function logDouble(x: number) {
console.log(x * 2);
}
// Explicit type requires return
function triple(x: number): number { // Error without return!
x * 3; // This doesn't return
}Explicit return types are valuable for:
- Public APIs (enforces contract)
- Complex functions (catches missing returns)
- Documentation (makes intent clear)
### Promises and Callbacks
Nested functions require careful attention:
// WRONG - inner return doesn't count
function fetchData(): Promise<Data> {
fetch("/api/data").then(res => {
return res.json(); // This returns from the callback, not fetchData
});
// Missing: return statement for fetchData itself
}
// CORRECT - return the promise
function fetchData(): Promise<Data> {
return fetch("/api/data").then(res => res.json());
}
// CORRECT - use async/await
async function fetchData(): Promise<Data> {
const res = await fetch("/api/data");
return res.json();
}### Union Return Types
For functions that can return different types:
function find(id: string): User | undefined {
const user = database.get(id);
if (user) {
return user;
}
return undefined; // Explicit return for clarity
}
// Or more concisely
function find(id: string): User | undefined {
return database.get(id) ?? undefined;
}### Generator Functions
Generators have special return type requirements:
// Generator must use yield, not return (for values)
function* generateNumbers(): Generator<number, void, unknown> {
yield 1;
yield 2;
yield 3;
// Optional: return; (returns undefined by default)
}
// Generator with explicit return value
function* withReturn(): Generator<number, string, unknown> {
yield 1;
yield 2;
return "done"; // Returns string
}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