A subclass can be used anywhere its base class is expected, so each member it overrides has to fit the base member's type: a method returning a number cannot be overridden by one returning a string. TS2416 names the member and both types, and the lines under it say what differs.
In a .js file the subclass's types come from its JSDoc and from inference, so an override with no annotations is checked against the base class through what its body returns.
At a glance
| Code | TS2416 |
|---|---|
| Message | Property '{0}' in type '{1}' is not assignable to the same property in base type '{2}'. |
| TypeScript's category | error |
| Listed under | Classes, JSX and unused code |
| Turned on by | No option: every checked file reports it |
| In .js files | Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file |
The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0}, {1} and {2} are filled in by tsc for each error.
What it looks like
class Shape {
/** @returns {number} */
area() {
return 0;
}
}
export class Square extends Shape {
/** @param {number} side */
constructor(side) {
super();
this.side = side;
}
area() {error TS2416: Property 'area' in type 'Square' is not assignable to the same property in base type 'Shape'.
Type '() => string' is not assignable to type '() => number'.
Type 'string' is not assignable to type 'number'.
return `${this.side ** 2} square meters`;
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["**/*.js"]
} What causes it
- An override returns a different type, as in the example.
- An override takes a narrower parameter type than the base method accepts.
- A class claims to implement an interface with
@implementsand a method's parameter type does not fit.
How to fix it
Make the override compatible
Return the base type (here a number) and put the formatting in a new method. If the base class's type is what is wrong, change it there.
class Shape {
/** @returns {number} */
area() {
return 0;
}
}
export class Square extends Shape {
/** @param {number} side */
constructor(side) {
super();
this.side = side;
}
area() {
return this.side ** 2;
}
label() {
return `${this.area()} square meters`;
}
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["**/*.js"]
} Gotchas
- Parameters are compared both ways for methods written as methods (TypeScript's method bivariance), so a narrower parameter in a method override is often accepted; property-style function types are checked strictly under
strictFunctionTypes.
Sources
- Classes: overriding methods · TypeScript handbook
- diagnosticMessages.json (TypeScript 5.9.3) · TypeScript on GitHub
Last reviewed 21 October 2026.