TypeError: x is not a function means the code put brackets after something that is not a function — a number, a string, an object, or, most often, undefined. The name before “is not a function” tells you exactly what you tried to call. From there it is one of five situations, and each has a distinct fix.
Reading the message#
const total = 42;
total(); // TypeError: total is not a function
const user = { name: 'Ann' };
user.greet(); // TypeError: user.greet is not a function
items.forEach(fn); // TypeError: items.forEach is not a function
The part before “is not a function” is the expression you called. If it is user.greet, then user exists but has no greet. If it is items.forEach, then items exists but is not the kind of thing with a forEach. Log the object and check its type:
console.log(typeof items, items);
console.log(Array.isArray(items));
Cause 1: the method does not exist on that type#
The most common shape. The value is real but of a different type than the code assumes:
| Error | Likely value | Fix |
|---|---|---|
items.map is not a function |
an object, or a string | Use Object.values(items).map, or check the API returns an array |
list.forEach is not a function |
a NodeList from querySelectorAll in an old browser, or an HTMLCollection | Array.from(list).forEach |
str.push is not a function |
a string, not an array | Split first, or use concatenation |
date.getFullYear is not a function |
a string that looks like a date | new Date(str) first |
json.map is not a function |
the raw response, not the parsed body | await response.json() first |
The API-returns-an-object case is worth a special look. Many APIs wrap their list: { results: [...] }. The code maps over the wrapper instead of results.
Cause 2: .then is not a function#
function getUser() {
return { name: 'Ann' }; // returns a plain object
}
getUser().then(u => ...); // TypeError: getUser(...).then is not a function
Only promises have .then. The function is synchronous, or it forgot to return the promise:
function getUser() {
fetch('/api/user'); // the promise is created and thrown away
}
getUser().then(...) // undefined.then - not a function
function getUser() {
return fetch('/api/user'); // return it
}
A missing return inside a .then chain produces the same error one step later. async functions always return a promise, which makes this class of bug disappear.
Cause 3: import and export mismatches#
// util.js
export function format(n) { ... }
// app.js
import format from './util.js'; // default import - but there is no default export
format(3); // TypeError: format is not a function
A default import of a module with only named exports gives undefined. Match the style: import { format } from './util.js'. The reverse mistake, import { x } from a module that only has a default export, does the same. In CommonJS, const format = require('./util') when the module exports an object gives you the object, and format() fails; you wanted require('./util').format.
Circular imports produce a subtler version: the module is imported before it has finished loading, so the function is temporarily undefined. Restructure so the cycle is broken.
Cause 4: a shadowed or reassigned name#
function process(items) {
const filter = items.length > 10; // reuses the name of a function elsewhere
return filter(items); // TypeError: filter is not a function
}
A local variable with the same name as a function, or a function accidentally reassigned (fn = fn() instead of result = fn()), replaces it with a value. Look for the name being declared or assigned nearby.
Cause 5: calling too early or on the wrong object#
const el = document.querySelector('.btn');
el.addEventListener('click', ...); // fine
document.querySelector('.btn').on('click', ...); // .on is jQuery, not DOM
Methods from a library used on plain objects, jQuery methods on DOM elements, or a plugin method called before the plugin script loaded. Confirm the object really is the type the method belongs to, and that the script defining the method comes first.
The optional-call operator#
onSuccess?.(result); // calls onSuccess only if it is defined
Use this for genuinely optional callbacks — a configuration object where the handler may be omitted. As with optional chaining, do not use it to hide a function that should exist.
Questions people ask#
Why does it say “is not a function” rather than “does not exist”?
Because reading a missing property gives undefined without complaint; only calling it fails. The property read succeeded, the call did not.
What about “is not a constructor”?
The same idea with new: you used new on something that is not a class or constructor function, often because of the same import mismatch.
Does TypeScript prevent this?
Mostly. It catches calling a non-function and calling a method that does not exist on the declared type. It cannot catch an API returning a different shape than its type says, which is why validating at the edge still matters.
The function is definitely defined; why does it fail intermittently?
Timing. The script that defines it loads after the script that calls it, or a circular import leaves it undefined on some load orders. Check the script order and the module graph.
Where to go next#
- “Cannot read properties of undefined” — the companion error.
- “x is not a constructor” — the same mistake with new.
- Promises explained with examples — for the .then case.