How to fix Interface declaration cannot declare a class field in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 11, 2025· 81 views

This TypeScript error occurs when you try to declare a class field (property with an initializer) inside an interface, which is not allowed. Interfaces can only define the shape of objects, not implement them. The fix involves converting the interface to a class or removing the field initializer.

Quick fix

Fixes TS1166

// WRONG - interface with field initializer
interface User {
  name: string = "Guest";  // Error: Interface declaration cannot declare a class field
  age: number = 0;         // Error
}

// CORRECT - interface without initializers
interface User {
  name: string;  // Type only, no value
  age: number;   // Type only, no value
}

// Usage
const user: User = {
  name: "John",  // Value assigned when creating object
  age: 30,
};
  1. Remove field initializers from the interface
  2. Convert the interface to a class if you need default values
  3. Use optional properties with union types for conditional defaults
  4. Use type aliases with intersection types for complex defaults
typescript — error
// WRONG - interface with field initializer
interface User {
name: string = "Guest"; // Error: Interface declaration cannot declare a class field
age: number = 0; // Error
}
// CORRECT - interface without initializers
interface User {
name: string; // Type only, no value
age: number; // Type only, no value
}
// Usage
const user: User = {
name: "John", // Value assigned when creating object
age: 30,
};
Interface declaration cannot declare a class field

What this error means

The "Interface declaration cannot declare a class field" error (TS1166) appears when you attempt to include a class field with an initial value inside a TypeScript interface. Interfaces in TypeScript are purely structural contracts that define what properties and methods an object should have, but they cannot contain implementations or initial values. Interfaces serve as type definitions only - they describe the shape of data but don't actually create that data. When you try to give a property an initial value (like `name: string = "default"`), you're mixing interface declaration syntax with class implementation syntax. This error commonly occurs when developers: 1. Confuse interfaces with abstract classes 2. Try to add default values to interface properties 3. Copy class syntax into an interface declaration 4. Use interfaces where they need concrete implementations TypeScript enforces this separation to maintain clear boundaries between type definitions (interfaces) and runtime implementations (classes).

Typical symptoms

  • Compilation fails with 'Interface declaration cannot declare a class field'
  • Error appears on interface properties that have initial values (= 'value')
  • IDE shows red underlines on interface property declarations with assignments
  • Error code TS1166 appears in compiler output
  • Code works when interface is converted to a class
  • The error occurs specifically on properties with '=' assignment operators

Common causes

  1. Adding an initial value to an interface property (e.g., `name: string = 'default'`)
  2. Confusing interface syntax with class property syntax
  3. Trying to define default values in an interface, which isn't supported
  4. Copy-pasting class code into an interface without removing implementations
  5. Using an interface where you need a concrete class with default values
  6. Mistaking interfaces for abstract classes, which can have implementations

How to fix "Interface declaration cannot declare a class field"

1Remove field initializers from the interface

The simplest fix is to remove the assignment from the interface property:

typescript
// WRONG - interface with field initializer
interface User {
  name: string = "Guest";  // Error: Interface declaration cannot declare a class field
  age: number = 0;         // Error
}

// CORRECT - interface without initializers
interface User {
  name: string;  // Type only, no value
  age: number;   // Type only, no value
}

// Usage
const user: User = {
  name: "John",  // Value assigned when creating object
  age: 30,
};

Interfaces define types, not values. The actual values must be provided when creating objects that implement the interface.

2Convert the interface to a class if you need default values

If you need default values, use a class instead:

typescript
// WRONG - trying to set defaults in interface
interface Config {
  timeout: number = 5000;
  retries: number = 3;
}

// CORRECT - use a class with default values
class Config {
  timeout: number = 5000;
  retries: number = 3;

  constructor(overrides?: Partial<Config>) {
    Object.assign(this, overrides);
  }
}

// Usage
const config = new Config();  // timeout=5000, retries=3
const customConfig = new Config({ timeout: 10000 });  // timeout=10000, retries=3

Classes support field initializers, constructors, and methods - everything you need for concrete implementations.

3Use optional properties with union types for conditional defaults

If you want optional properties with fallback values, use union types:

typescript
// WRONG
interface Settings {
  theme: "light" | "dark" = "light";
  notifications: boolean = true;
}

// CORRECT - make properties optional, handle defaults elsewhere
interface Settings {
  theme?: "light" | "dark";
  notifications?: boolean;
}

// Helper function to apply defaults
function createSettings(overrides?: Settings): Required<Settings> {
  return {
    theme: overrides?.theme ?? "light",
    notifications: overrides?.notifications ?? true,
  };
}

// Usage
const settings = createSettings();  // theme="light", notifications=true
const darkSettings = createSettings({ theme: "dark" });  // theme="dark", notifications=true

This pattern separates the type definition (interface) from the default value logic (helper function).

4Use type aliases with intersection types for complex defaults

For more complex scenarios, use type aliases with intersection types:

typescript
// Define base interface without defaults
interface UserBase {
  name: string;
  email: string;
}

// Define defaults as a separate type
type UserDefaults = {
  role: "user" | "admin";
  active: boolean;
};

// Create a type that combines them
type UserWithDefaults = UserBase & Partial<UserDefaults>;

// Helper to apply defaults
function createUser(base: UserBase, defaults?: Partial<UserDefaults>): UserBase & UserDefaults {
  return {
    ...base,
    role: defaults?.role ?? "user",
    active: defaults?.active ?? true,
  };
}

// Usage
const user = createUser(
  { name: "Alice", email: "alice@example.com" },
  { role: "admin" }
);
// Result: { name: "Alice", email: "alice@example.com", role: "admin", active: true }

This approach gives you flexibility while keeping type definitions clean.

5Consider abstract classes for shared implementations

If you need to share implementation between classes, use abstract classes:

typescript
// WRONG - trying to share implementation via interface
interface Logger {
  prefix: string = "[LOG]";
  log(message: string): void;
}

// CORRECT - use abstract class
abstract class Logger {
  protected prefix: string = "[LOG]";

  abstract log(message: string): void;

  protected formatMessage(message: string): string {
    return `${this.prefix} ${new Date().toISOString()}: ${message}`;
  }
}

// Concrete implementation
class ConsoleLogger extends Logger {
  log(message: string): void {
    console.log(this.formatMessage(message));
  }
}

// Another implementation
class FileLogger extends Logger {
  constructor() {
    super();
    this.prefix = "[FILE]";
  }

  log(message: string): void {
    // Write to file using formatted message
    const formatted = this.formatMessage(message);
    // ... file writing logic
  }
}

Abstract classes can have field initializers and method implementations that subclasses inherit.

6Use factory functions with interface return types

Combine interfaces with factory functions for maximum flexibility:

typescript
// Interface defines the shape
interface ApiClient {
  baseUrl: string;
  timeout: number;
  get<T>(path: string): Promise<T>;
  post<T>(path: string, data: unknown): Promise<T>;
}

// Factory function creates instances with defaults
function createApiClient(config?: Partial<Pick<ApiClient, 'baseUrl' | 'timeout'>>): ApiClient {
  const baseUrl = config?.baseUrl ?? "https://api.example.com";
  const timeout = config?.timeout ?? 30000;

  return {
    baseUrl,
    timeout,
    async get<T>(path: string): Promise<T> {
      const response = await fetch(`${baseUrl}${path}`, { timeout });
      return response.json();
    },
    async post<T>(path: string, data: unknown): Promise<T> {
      const response = await fetch(`${baseUrl}${path}`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(data),
        timeout,
      });
      return response.json();
    },
  };
}

// Usage
const defaultClient = createApiClient();  // Uses all defaults
const customClient = createApiClient({
  baseUrl: "https://staging.example.com",
  timeout: 60000
});

This pattern gives you both: clean interface definitions and configurable defaults.

Advanced notes

### Interface vs Type Alias

Both interfaces and type aliases cannot have field initializers, but they have different capabilities:

typescript
// Interface - can be extended, merged
interface Point {
  x: number;
  y: number;
}

interface Point3D extends Point {
  z: number;
}

// Type alias - more flexible with unions, intersections
type ID = string | number;
type Coordinates = { x: number; y: number };
type Point3D = Coordinates & { z: number };

// NEITHER can have field initializers
interface Bad {
  x: number = 0;  // Error
}

type AlsoBad = {
  y: number = 0;  // Error
};

### Declaration Merging

Interfaces support declaration merging, which can be confusing when you see multiple declarations:

typescript
// First declaration
interface Config {
  timeout: number;
}

// Second declaration (merged)
interface Config {
  retries: number;
}

// Result: Config has both timeout and retries
const config: Config = {
  timeout: 5000,
  retries: 3,
};

// But still can't add initializers
interface Config {
  timeout: number = 5000;  // Error even in merged declaration
}

### Readonly Properties

Interfaces can have readonly properties, but still no initializers:

typescript
interface Circle {
  readonly radius: number;
  readonly diameter: number;  // Can be computed, but not initialized here

  // WRONG
  readonly pi: number = 3.14;  // Error

  // CORRECT - compute in implementation
}

class CircleImpl implements Circle {
  readonly radius: number;
  readonly diameter: number;
  readonly pi: number = 3.14;  // OK in class

  constructor(radius: number) {
    this.radius = radius;
    this.diameter = radius * 2;
  }
}

### Generic Interfaces

Generic interfaces also cannot have field initializers:

typescript
interface Container<T> {
  value: T;
  // WRONG
  defaultValue: T = null;  // Error - can't initialize generic type

  // CORRECT
  defaultValue?: T;  // Optional instead
}

// Factory pattern for generics
function createContainer<T>(value: T, defaultValue?: T): Container<T> {
  return {
    value,
    defaultValue: defaultValue ?? null as any,  // Type assertion if needed
  };
}

### Interface vs Abstract Class Decision Tree

Use this guide to choose between interfaces and abstract classes:

1. Use Interface when:
- Defining a contract for external code
- Need declaration merging
- Defining object shapes for libraries/APIs
- Lightweight type checking only

2. Use Abstract Class when:
- Need to share implementation between classes
- Want default method implementations
- Need field initializers
- Building a class hierarchy
- Need constructor logic

3. Use Regular Class when:
- Creating concrete instances
- Need full implementation
- Using with new keyword
- Runtime behavior needed

### Compiler Flags

TypeScript has compiler flags that affect interface behavior:

json
{
  "compilerOptions": {
    "strict": true,  // Enables all strict checks
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true  // Requires explicit initialization in classes
  }
}

The strictPropertyInitialization flag is particularly relevant - it requires class properties to be initialized in the constructor, which might lead developers to incorrectly try initializing interface properties instead.

### Migration from JavaScript

JavaScript developers transitioning to TypeScript often make this error when converting plain object patterns:

javascript
// JavaScript pattern
const defaults = {
  timeout: 5000,
  retries: 3,
};

// Incorrect TypeScript translation
interface Defaults {
  timeout: number = 5000;  // Error
  retries: number = 3;
}

// Correct TypeScript translation
interface Defaults {
  timeout: number;
  retries: number;
}

const defaults: Defaults = {
  timeout: 5000,
  retries: 3,
};

Remember: Interfaces define types, objects hold values.

Frequently asked questions

What does "TS1166" mean?
The "Interface declaration cannot declare a class field" error (TS1166) appears when you attempt to include a class field with an initial value inside a TypeScript interface. Interfaces in TypeScript are purely structural contracts that define what properties and methods an object should have, but they cannot contain implementations or initial values. Interfaces serve as type definitions only - they describe the shape of data but don't actually create that data.
How do I fix "TS1166"?
This TypeScript error occurs when you try to declare a class field (property with an initializer) inside an interface, which is not allowed. Interfaces can only define the shape of objects, not implement them. The fix involves converting the interface to a class or removing the field initializer.
What causes "TS1166"?
Adding an initial value to an interface property (e.g., name: string = 'default') Confusing interface syntax with class property syntax Trying to define default values in an interface, which isn't supported
Is the "TS1166" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading