TypeScript in strict mode prevents calling functions or methods that could be undefined. This error occurs when optional function properties or return values lack proper null/undefined checks before invocation.
This TypeScript error (TS2722) occurs when you attempt to call a function or method that TypeScript cannot guarantee exists. In strict mode, TypeScript treats null and undefined as distinct types and requires explicit handling. When you have a variable or property whose type includes undefined (like optional parameters, optional properties, or functions that might return undefined), TypeScript prevents you from directly invoking or calling it without first checking that it's not undefined. This error is a type safety mechanism designed to prevent the common JavaScript runtime error "Cannot read property of undefined" by catching the problem at compile time instead.
The optional chaining operator (?.) combined with optional call (?.) safely handles undefined values. If the function is undefined, the expression returns undefined instead of throwing an error.
// Before: Error - possibly undefined
const callback: ((value: number) => void) | undefined = getCallback();
callback(42); // TS2722 error
// After: Using optional chaining with optional call
callback?.(42); // Safe - returns undefined if callback is undefined
// Optional call on object property
const config: { onSave?: (data: any) => void } = getConfig();
config.onSave?.({ /* data */ }); // Safe invocationOptional chaining short-circuits when it encounters undefined or null, preventing the call from executing and returning undefined instead.
Before calling the function, explicitly check that it is not undefined or null. This is a classic type narrowing approach.
// Before: Error - possibly undefined
const handler: ((event: Event) => void) | undefined = getEventHandler();
handler(event); // TS2722 error
// After: Explicit null check
if (handler !== undefined) {
handler(event); // Type narrowed to (event: Event) => void
}
// Alternative: Check both null and undefined
if (handler != null) {
handler(event);
}
// Using logical AND operator
handler && handler(event); // Only calls if handler is truthyThe type narrowing ensures TypeScript understands that inside the if block, handler cannot be undefined.
The typeof operator helps verify that a value is actually a function before attempting to call it.
// Before: Error - possibly undefined
const maybeFn: Function | undefined = getValue();
maybeFn(); // TS2722 error
// After: Using typeof check
if (typeof maybeFn === 'function') {
maybeFn(); // Now TypeScript knows it's a function
}
// With more complex types
const obj: { compute?: (x: number) => number } = getObject();
if (typeof obj.compute === 'function') {
const result = obj.compute(10); // Safe to call
}The typeof check narrows the type from "possibly undefined" to definitely a function.
If a function is optional but you need a fallback, use the nullish coalescing operator (??) or logical OR (||) to provide a default.
// Before: Error - possibly undefined
const callback: ((data: any) => void) | undefined = config.onSuccess;
callback(data); // TS2722 error
// After: Provide default function
const handler = callback ?? (() => {}); // Use empty function if callback is undefined
handler(data); // Safe - always a function
// Alternative with logical OR
const onSuccess = config.onSuccess || ((result) => console.log(result));
onSuccess(data);
// Inline arrow function as default
const processCallback = (cb: ((x: number) => string) | undefined) => {
const actual = cb ?? (x => x.toString());
return actual(42); // Always valid
};This approach ensures the variable is never undefined before calling it.
Instead of marking a parameter as optional (possibly undefined), provide a default function value.
// Before: Error - onComplete might be undefined
function fetchData(url: string, onComplete?: (result: any) => void) {
fetch(url)
.then(r => r.json())
.then(data => onComplete(data)); // TS2722 error
}
// After: Provide default function
function fetchData(url: string, onComplete: (result: any) => void = () => {}) {
fetch(url)
.then(r => r.json())
.then(data => onComplete(data)); // Safe - onComplete is always a function
}
// Alternative: Create wrapper to handle undefined
function fetchData(url: string, onComplete?: (result: any) => void) {
fetch(url)
.then(r => r.json())
.then(data => {
if (onComplete) {
onComplete(data); // Type narrowed in this scope
}
});
}Default values eliminate the undefined state, simplifying your code.
If you know a function is always present, remove undefined from the type annotation. If it might not exist, handle it with the techniques above.
// Before: Type includes undefined
interface EventConfig {
onEvent?: (event: Event) => void; // May be undefined
}
function triggerEvent(config: EventConfig, event: Event) {
config.onEvent(event); // TS2722 error
}
// After: Mark as required or use proper guards
interface EventConfig {
onEvent: (event: Event) => void; // Required - never undefined
}
function triggerEvent(config: EventConfig, event: Event) {
config.onEvent(event); // Safe - always defined
}
// Or keep optional but handle it
function triggerEvent(config: EventConfig, event: Event) {
config.onEvent?.(event); // Optional chaining handles undefined
}Clearing up type annotations makes the intent of your code explicit.
Ensure strict null checking is enabled in your TypeScript configuration to catch these issues early.
{
"compilerOptions": {
"strict": true,
"strictNullChecks": true,
"target": "ES2020",
"module": "ESNext"
}
}Or use the "strict" option which enables strictNullChecks and other safety checks:
{
"compilerOptions": {
"strict": true
}
}Strict mode catches these errors at compile time rather than runtime.
Verify that your changes resolve the TS2722 error by running the TypeScript compiler.
# Check for TypeScript errors
npx tsc --noEmit
# Or using npm/yarn scripts
npm run type-check
yarn type-check
# Watch mode during development
npx tsc --watchThe error should no longer appear once your code properly handles possibly undefined values.
Optional Chaining vs. Logical AND: Optional chaining (?.) is the preferred modern approach (ES2020+) because it's more explicit and handles all falsy values gracefully. Logical AND (&&) also works but checks for all falsy values, not just null/undefined. Use optional chaining when targeting modern environments.
Type Narrowing with Function Types: TypeScript's type narrowing only works within the scope where the check occurs. If you check inside an if block, the narrowed type only applies within that block. Outside, the original type (with undefined) applies again.
Function Overloading: For complex functions with optional callbacks, consider using function overloads to explicitly specify which parameter combinations are valid:
function process(data: string, callback: (result: string) => void): void;
function process(data: string): string;
function process(data: string, callback?: (result: string) => void) {
const result = transform(data);
callback?.(result);
return result;
}Assertion Functions: TypeScript 3.7+ supports assertion functions to verify conditions:
function assertIsFunction(value: unknown): asserts value is Function {
if (typeof value !== 'function') {
throw new Error('Value must be a function');
}
}
const callback: (() => void) | undefined = getCallback();
assertIsFunction(callback);
callback(); // Safe - TypeScript knows it's a functionStrict Mode Trade-offs: While strict mode prevents bugs, it requires more explicit type handling. The initial verbosity pays off in long-term maintainability and fewer runtime errors.
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