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

  1. Replace the arrow with a class expression or a regular function expression if the value must be constructible.
  2. Audit call sites that apply `new` to dynamic values and make sure only classes/constructor functions reach them.
  3. 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

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.