This error occurs when you attempt to use the 'new' keyword with a value that TypeScript doesn't recognize as constructable (doesn't have a construct signature). The fix involves either converting constructor functions to classes, properly typing the constructor, or using type assertions.
This TypeScript error indicates you're trying to instantiate something with the 'new' keyword, but TypeScript's type system doesn't recognize it as a constructor. In TypeScript, a "construct signature" is the type definition that tells TypeScript "this thing can be instantiated with the new keyword." Classes have construct signatures by default, but plain functions, interfaces, and other types often don't unless explicitly defined. Common scenarios that trigger this error: 1. Trying to use 'new' with a regular function instead of a class 2. Using 'new' with an imported value that's not properly typed as a constructor 3. Attempting to instantiate a type or interface directly 4. Dynamic imports or values that lose type information TypeScript's recommendation is to convert constructor functions to proper classes, as they provide better type safety and clearer intent.
The simplest and most recommended solution is to convert your constructor function to a TypeScript class:
Before (causes TS2351):
function Employee(fullName: string, salary: number) {
this.fullName = fullName;
this.salary = salary;
}
// TS2351 Error here:
const emp1 = new Employee('Bobby Hadz', 100);After (no error):
class Employee {
constructor(
public fullName: string,
public salary: number,
) {}
getSalary() {
return this.salary;
}
}
// Works correctly:
const emp1 = new Employee('Bobby Hadz', 100);Classes are TypeScript's preferred way to define constructors because they:
- Have explicit construct signatures
- Support inheritance clearly
- Work with access modifiers (public, private, protected)
- Are well understood by type checkers
If you must use a constructor function (not recommended), explicitly type the 'this' parameter:
With typed 'this':
function Employee(this: any, fullName: string, salary: number) {
this.fullName = fullName;
this.salary = salary;
this.getSalary = function () {
return this.salary;
};
}
// Still requires type assertion:
const emp1 = new (Employee as any)('Bobby Hadz', 100);This tells TypeScript that 'this' will be an object where you can set properties. However, you still need to use a type assertion (as any) with the 'new' keyword because constructor functions alone don't have construct signatures.
Better approach - use a constructor interface:
interface EmployeeConstructor {
new (fullName: string, salary: number): Employee;
}
interface Employee {
fullName: string;
salary: number;
getSalary(): number;
}
function Employee(this: Employee, fullName: string, salary: number) {
this.fullName = fullName;
this.salary = salary;
}
Employee.prototype.getSalary = function () {
return this.salary;
};
// Type the variable as the constructor interface:
const EmployeeConstructor: EmployeeConstructor = Employee as any;
const emp1 = new EmployeeConstructor('Bobby Hadz', 100);Sometimes the error occurs because you're using 'new' unnecessarily. Check if the value is actually a constructor:
Example - NextResponse.json() is a static method:
// Wrong - NextResponse.json() is not a constructor:
return new NextResponse.json({ message: 'Hello' });
// Correct - call it as a regular function:
return NextResponse.json({ message: 'Hello' });Common cases where 'new' is unnecessary:
- Static factory methods: new Class.create() → Class.create()
- Utility functions: new getConfig() → getConfig()
- Module exports that are functions: new myFunction() → myFunction()
Check the library documentation to verify if something should be instantiated with 'new' or called directly.
If you're working with a function type that needs to be constructable, add a construct signature:
For function types:
// Define what 'new' should do with this function
type Constructor = new (name: string) => { name: string };
// This function matches the Constructor type:
function MyClass(this: { name: string }, name: string) {
this.name = name;
}
// Use with proper typing:
const MyClassConstructor: Constructor = MyClass as any;
const instance = new MyClassConstructor('test');For class-like objects:
// Define both static and instance types
interface MyClassStatic {
new (): MyClassInstance;
}
interface MyClassInstance {
method(): void;
}
// Declare the variable with the static interface:
declare const MyClass: MyClassStatic;
const instance = new MyClass();When dealing with dynamically imported or computed values, use type assertions:
For dynamically loaded classes:
async function loadPlugin(pluginName: string) {
const module = await import(`./plugins/${pluginName}`);
// Type assert as a constructor
type AnyConstructor = new (...args: any[]) => any;
const PluginClass = module.default as AnyConstructor;
// Now 'new' works without error:
const instance = new PluginClass();
return instance;
}For values from external libraries:
import { someValue } from 'some-library';
// If someValue should be constructable, assert it:
type Constructable<T> = new (...args: any[]) => T;
const Constructor = someValue as Constructable<MyType>;
const instance = new Constructor();Be cautious with type assertions - they tell TypeScript to trust your typing. Use only when you're certain about the actual runtime type.
If the error occurs with an imported library, verify the types are correct:
Check if types are installed:
# For popular libraries, @types packages provide TypeScript definitions
npm install --save-dev @types/some-libraryVerify the import is correct:
// Wrong - importing the namespace instead of the class:
import * as MyClass from './MyClass';
const instance = new MyClass(); // Error
// Correct - import the class directly:
import MyClass from './MyClass';
const instance = new MyClass(); // OKCheck type definition file:
// Look at the .d.ts file or source to verify it exports a class/constructor
// For example, in node_modules/@types/some-lib/index.d.ts:
// If it's exported as class:
export class SomeClass { }
// If it's exported as namespace:
export namespace SomeClass { }
// The second case won't work with 'new'Update your tsconfig.json to catch these issues earlier:
{
"compilerOptions": {
// Enable strict mode for better type safety
"strict": true,
// More granular strict options:
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitAny": true,
"noImplicitThis": true,
// Helps catch constructor issues:
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
// Module resolution:
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}With strict mode enabled, TypeScript will catch missing constructor signatures earlier in development rather than at runtime.
### Understanding Construct Signatures
A construct signature tells TypeScript that something can be instantiated with 'new'. In the type system, it looks like:
// Construct signature in an interface
interface Constructor {
new (arg: string): Instance;
}
// This is different from a regular function signature
interface CallableFunction {
(arg: string): Instance;
}Classes automatically have construct signatures for their constructors. Regular functions don't unless you explicitly define them.
### Generic Constructor Constraints
When working with generics, ensure the type parameter is constrained to be constructable:
// Without constraint - doesn't work with 'new':
function create<T>(TypeClass: T): T {
return new TypeClass(); // Error
}
// With constructor constraint - works correctly:
function create<T>(TypeClass: new () => T): T {
return new TypeClass();
}
// Usage:
class MyClass {
name = 'test';
}
const instance = create(MyClass); // OK### Factory Pattern as Alternative
If constructors are causing type issues, consider the factory pattern:
// Instead of class with problematic constructor:
class Config {
private constructor(private data: any) {}
// Factory method instead:
static create(data: any): Config {
return new Config(data);
}
}
// Usage is cleaner and types better:
const config = Config.create({ ... });### NextResponse and Framework-Specific Examples
In Next.js, many utilities are factory functions, not constructors:
// NextResponse is a factory function, not a constructor:
import { NextResponse } from 'next/server';
// WRONG:
new NextResponse.json({ message: 'OK' }); // Error
// CORRECT:
NextResponse.json({ message: 'OK' });
NextResponse.next();
NextResponse.redirect('/path');### Debugging Type Issues
If you're unsure whether something is constructable, check at the source:
// TypeScript can show you the type:
import SomeValue from 'some-lib';
// Hover over SomeValue in VS Code to see its type
// Or use this to force an error that shows the type:
type Check = typeof SomeValue;
// Check if it's callable vs constructable:
const test1: typeof SomeValue = (arg) => {}; // If assignable, it's callable
const test2: new () => any = SomeValue; // If assignable, it's constructable### Common Library Patterns
Different libraries use different patterns:
Classes (constructable):
import * as path from 'path'; // No - just utilities
import express from 'express'; // No - it's a factory function
const app = express(); // Call it
class MyClass { } // Yes - constructable
const instance = new MyClass();Factory patterns (not constructable):
// Stripe SDK uses functions, not classes:
const stripe = require('stripe')(apiKey);
// Not: new require('stripe')(apiKey)
// React uses both functions and classes:
const element = React.createElement(...); // Factory
class MyComponent extends React.Component {} // Class### TypeScript Error Messages
Error TS2351 appears in different contexts:
- "Cannot use 'new' with an expression whose type lacks a construct signature" - main error
- "Cannot find name 'SomeValue'" - type not found
- "Type 'X' is not constructable" - similar error with different wording
All indicate the same issue: trying to use 'new' with something TypeScript doesn't recognize as a constructor.
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