Why does `new (() => {})()` throw a TypeError in JavaScript, and which other function forms are non-constructible in the same way?
answer
- callable is not the same as constructible
- new needs an internal capability
- [[Construct]] versus [[Call]]
- arrows have no prototype property
- shorthand methods are in the same group
basics
~20 sArrow functions are callable but not constructible: they have no [[Construct]] internal method and no prototype property, so new throws a TypeError. Concise object and class methods, getters and setters, generator functions and async functions are non-constructible too.
solid answer
~40 s`new` requires the callee to have the internal `[[Construct]]` method. Arrow functions only have `[[Call]]`, so the engine rejects the expression with something like "is not a constructor" - and consistently, an arrow has no `prototype` property, because there would be nothing to link an instance to. The same non-constructible group includes shorthand methods in object literals and class bodies, getters and setters, generator functions, async functions and async arrows. Constructible callables are ordinary `function` declarations and expressions, `class` declarations, and bound functions whose target is itself constructible. The everyday consequence is that you cannot convert a constructor to an arrow to "modernise" it, and a method written as `{ m() {} }` is not interchangeable with `{ m: function () {} }` if anyone calls it with `new`.
code
javascript · 12 linesconst arrow = () => {};
try { new arrow(); } catch (e) { console.log(e.constructor.name); } // TypeError
const api = { shorthand() {}, assigned: function () {} };
try { new api.shorthand(); } catch (e) { console.log(e.constructor.name); } // TypeError
console.log(new api.assigned() instanceof Object); // true
function Point(x) { this.x = x; }
const Bound = Point.bind(null);
console.log(new Bound(5).x); // 5
console.log('prototype' in arrow, 'prototype' in Point); // false truego deeper
Know that arrow functions cannot be used with new and that doing so throws a TypeError. Be able to say arrows have no this of their own, which is why they cannot build one.
Explain the underlying split between [[Call]] and [[Construct]], and extend the non-constructible list beyond arrows to shorthand methods, getters, generators and async functions. Mention that arrows carry no prototype property as the visible symptom.
Show where this causes real incidents: a refactor to object-literal shorthand or to an arrow silently removes constructibility, and the failure surfaces only at the call site that uses new. Know how to probe constructibility with Reflect.construct when writing generic code.
Own the API-design angle: any library that accepts a user-supplied callable and calls it with new is quietly banning arrows and methods from its contract. Decide whether to construct at all, document the requirement, or accept a factory callback instead.
## Callable and constructible are two different capabilities Every function object in JavaScript has an internal `[[Call]]` method - that is what makes it callable. Only *some* function objects also have `[[Construct]]`, and that is what `new` demands. When you write `new F()`, the engine checks for `[[Construct]]` first; if it is absent, evaluation stops with a `TypeError` such as `F is not a constructor`. An arrow function is deliberately built without `[[Construct]]`: ```js const arrow = () => {}; new arrow(); // TypeError: arrow is not a constructor ``` ## Why arrows in particular Arrow functions were designed as lightweight lambdas that do not carry their own `this` - they close over the surrounding one. A constructor's whole job is to receive a brand-new `this`, so the two designs are incompatible: there is no slot to put the new object in. Consistently, arrows also carry no `prototype` property: ```js const arrow = () => {}; function Point(x) { this.x = x; } 'prototype' in arrow; // false 'prototype' in Point; // true ``` That property exists only to be used as the `[[Prototype]]` of instances a constructor creates. No construction, no need for it. ## The full non-constructible list - **Arrow functions**, including async arrows. - **Concise (shorthand) methods** in object literals - `{ m() {} }` - and in class bodies, both prototype and `static` methods. - **Getters and setters** defined with `get` / `set`. - **Generator functions** (`function*`) and **async generators**. - **Async functions** (`async function`). - Most built-in functions that are not constructors, e.g. `Math.max` or `parseInt`. A nice detail: a generator function *does* have a `prototype` property - it is used as the prototype of the generator objects it returns - yet it is still not constructible. Async functions have no `prototype` property at all. So "has a prototype property" and "is a constructor" are correlated but not the same test. ## What is constructible - `function` declarations and `function` expressions, including named ones. - `class` declarations and expressions (which are *only* constructible - calling a class without `new` throws). - **Bound functions**, but only if the target is constructible: `Point.bind(null)` can be `new`ed and produces a `Point`; `arrow.bind(null)` cannot. ```js function Point(x) { this.x = x; } const Bound = Point.bind(null); new Bound(5).x; // 5 ``` ## The shorthand-method trap The most practical consequence is that object-literal shorthand is not a pure abbreviation: ```js const api = { Shorthand() {}, Assigned: function () {} }; new api.Assigned(); // fine new api.Shorthand(); // TypeError: api.Shorthand is not a constructor ``` They look interchangeable, and for ordinary calls they are, but only the assigned function expression carries `[[Construct]]`. Codemods and hand-refactors that "tidy up" object literals into shorthand have broken code that used one of those functions as a constructor. The mirror-image trap is converting a constructor to an arrow. `const User = (name) => { this.name = name; }` is not a slightly different constructor - it is not a constructor at all, and every `new User(...)` in the codebase now throws. ## How to check at runtime There is no direct `isConstructor` predicate. The idiomatic probe is to try constructing through `Reflect.construct` and catch: ```js function isConstructor(f) { try { Reflect.construct(function () {}, [], f); return true; } catch { return false; } } ``` This uses `f` as the *new target*, which requires it to be a constructor without actually running its body. In interviews it is enough to know the categories; the probe is a nice extra. ## What interviewers listen for The minimum is "arrow functions cannot be used with `new`". A stronger answer names the reason - no `[[Construct]]`, no own `this`, no `prototype` property - and extends the list to shorthand methods, generators and async functions, which is where the surprise actually costs people time.
- Can you use `new` on a generator function or an async function?No - neither has `[[Construct]]`, so both throw `TypeError: ... is not a constructor`. A generator function is interesting because it *does* have a `prototype` property, used as the prototype of the generator objects it returns, so the presence of `prototype` is not a reliable constructor test. Async functions have no `prototype` property at all.
- Why does an arrow function have no `prototype` property?Because that property exists solely to become the `[[Prototype]]` of instances the function constructs, and an arrow never constructs anything. Arrows also have no binding of their own for `this`, so there would be nowhere to place a newly created instance. The missing `prototype` is a visible symptom of the missing `[[Construct]]`.
- Is `f.bind(null)` constructible?It is if `f` is. A bound function exposes `[[Construct]]` exactly when its target does, and constructing it delegates to the target - so `new Point.bind(null)(5)` builds a real `Point`, with the bound `this` ignored and any pre-bound arguments still prepended. Binding an arrow function produces something that is still not constructible.
saying these in an interview costs you the question
- Says any function at all can be called with new
- Thinks shorthand methods and function-expression properties are fully interchangeable
- Claims arrow functions merely inherit an outer prototype
- Believes new on an arrow silently returns undefined
- Assumes having a prototype property proves something is a constructor