swc-project/swc · error · TypeError
Cannot instantiate an arrow function
Error message
Cannot instantiate an arrow function
What it means
Native arrow functions are not constructible, so `new arrowFn()` throws a TypeError in every engine. When SWC lowers async arrow functions (ES2017 async_to_generator) or spec-mode class-property arrows into ordinary functions, the output would otherwise silently accept `new`; SWC inserts `_new_arrow_check(this, _this)` at the top of the lowered body to preserve native semantics and throw `Cannot instantiate an arrow function`.
Source
Thrown at crates/swc_ecma_transforms_base/src/helpers/generated/_new_arrow_check.rs:11
// This file is generated by `cargo codegen helpers`. DO NOT MODIFY.
use super::{HelperDef, HelperName};
pub const DEF: HelperDef = HelperDef {
name: HelperName::new_arrow_check,
local: "_new_arrow_check",
import_path: "@swc/helpers/_/_new_arrow_check",
#[cfg(feature = "inline-helpers")]
source: r#"function _new_arrow_check(innerThis, boundThis) {
if (innerThis !== boundThis) throw new TypeError("Cannot instantiate an arrow function");
}
"#,
#[cfg(feature = "inline-helpers")]
deps: super::HelperBitmap::from_bits(0x00000000000000020000000000000000),
};
#[cfg(feature = "inline-helpers")]
pub fn stmts() -> &'static [swc_ecma_ast::Stmt] {
static STMTS: once_cell::sync::Lazy<Vec<swc_ecma_ast::Stmt>> =
once_cell::sync::Lazy::new(|| super::super::parse(DEF.source, DEF.import_path));
&STMTS
}
View on GitHub (pinned to 5176682b65)
Solutions
- Replace the arrow with a class expression or a regular function expression if the value must be constructible.
- Audit call sites that apply `new` to dynamic values and make sure only classes/constructor functions reach them.
- If the logic must stay an arrow, expose it via a wrapper class whose constructor delegates to the arrow.
Example fix
// before
const HttpClient = async (base) => { /* ... */ };
const client = new HttpClient('/api'); // TypeError: Cannot instantiate an arrow function
// after
class HttpClient {
constructor(base) { this.base = base; }
async request(path) { /* ... */ }
}
const client = new HttpClient('/api'); Defensive patterns
Strategy: type-guard
Validate before calling
// Before calling `new` on a dynamic value, check it is plausibly constructible.
// Native (and lowered) arrows lack a `prototype` property or are async/generator functions.
function isConstructible(fn) {
if (typeof fn !== 'function') return false;
if (!fn.prototype) return false; // native arrows, async and generator functions
return true;
}
const C = registry.get('client');
const client = isConstructible(C) ? new C() : C(); Type guard
const isConstructible = (fn: unknown): fn is new (...args: never[]) => unknown =>
typeof fn === 'function' && (fn as { prototype?: unknown }).prototype != null; Try / catch
try {
const inst = new Factory();
} catch (err) {
if (err instanceof TypeError && /arrow function/.test(err.message)) {
// Factory is an arrow function — call it instead of constructing
const inst = Factory();
} else throw err;
} Prevention
- Never register arrow functions with DI containers or factories that apply `new`.
- If a value must be constructed, define it with `class` — arrows are never constructible by design.
- Note: after transpilation an arrow becomes a plain function, so `f.prototype === undefined` only catches native arrows; author-time discipline is the reliable guard.
When it happens
Trigger: Applying `new` or `Reflect.construct` to a value that is an async arrow function after lowering, e.g. `const C = async () => {}; new C()`, or DI/event frameworks that do `new (resolve(key))()` where the resolved factory is an async arrow.
Common situations: Refactoring a class constructor into an async arrow while call sites still use `new`; generic factories or test utilities that construct any registered callable; wrapping constructors with arrow-based decorators and then constructing the result.
Related errors
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/fc9c42a4f7dbfbf3.
Report an issue: GitHub.