typescript type_error ai_generated true

TS2416: Property 'X' in type 'Y' is not assignable to the same property in base type 'Z'

ID: typescript/ts2416-property-not-assignable-base

Also available as: JSON · Markdown
90%Fix Rate
92%Confidence
55Evidence
2020-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
5 active

Root Cause

A class overrides a property or method from its base class (or implements an interface member) with an incompatible type. TypeScript enforces that overridden members maintain Liskov Substitution Principle compatibility -- the derived type must be assignable to the base type for that member. Common causes: narrowing a method parameter type in a subclass, changing a return type incompatibly, or overriding a property with a different type.

generic

Workarounds

  1. 92% success Widen the overriding member's type to be compatible with the base type using union or the base type itself
    // Before (error): method parameter is narrower than base
    class Base {
      handle(input: string | number): void { /* ... */ }
    }
    class Derived extends Base {
      handle(input: string): void { /* TS2416 */ }
    }
    
    // After: accept the full base type, narrow inside
    class Derived extends Base {
      handle(input: string | number): void {
        if (typeof input === 'string') {
          // string-specific logic
        }
      }
    }

    Sources: https://www.typescriptlang.org/docs/handbook/2/classes.html#overriding-methods

  2. 88% success Use generics in the base class to allow subclasses to specialize types safely
    // Before: rigid base type
    class Base {
      data: Record<string, unknown> = {};
    }
    class Derived extends Base {
      data: { name: string; age: number } = { name: '', age: 0 }; // TS2416
    }
    
    // After: generic base allows type-safe specialization
    class Base<T extends Record<string, unknown> = Record<string, unknown>> {
      data: T;
      constructor(data: T) { this.data = data; }
    }
    class Derived extends Base<{ name: string; age: number }> {
      constructor() { super({ name: '', age: 0 }); }
    }

    Sources: https://www.typescriptlang.org/docs/handbook/2/generics.html#generic-classes

  3. 80% success Use an interface with method overloads instead of class inheritance
    // Instead of class inheritance with incompatible overrides,
    // use an interface with overloaded signatures:
    interface Handler {
      handle(input: string): string;
      handle(input: number): number;
      handle(input: string | number): string | number;
    }
    
    class MyHandler implements Handler {
      handle(input: string): string;
      handle(input: number): number;
      handle(input: string | number): string | number {
        if (typeof input === 'string') return input.toUpperCase();
        return input * 2;
      }
    }

    Sources: https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads

Dead Ends

Common approaches that don't work:

  1. Use 'as any' type assertion on the overriding property to silence the error 80% fail

    Casting to any removes type safety at the override point, allowing runtime type mismatches. Code that uses the base class type will pass values the subclass cannot handle, causing runtime errors. This breaks polymorphism and the Liskov Substitution Principle that TypeScript is enforcing.

  2. Disable strictFunctionTypes in tsconfig to allow bivariant parameter checking 70% fail

    Disabling strictFunctionTypes weakens type checking across the entire project, not just the one override. It allows unsound assignments where a function expecting a broader type is assigned one expecting a narrower type. This masks real bugs elsewhere and does not specifically target TS2416 (which applies to property types, not just function types).

Error Chain

Leads to:
Preceded by:
Frequently confused with: