There is the example:
interface A {
method: (itself: this) => string;
}
interface B extends A {
additionalProperty: boolean;
}
type Extend<T extends A> = A & { extension: number };
type ExtendedA = Extend<A>
type ExtendedB = Extend<B>
When I try to extend B
TypeScript writes:
Type 'B' does not satisfy the constraint 'A'. Types of property 'method' are incompatible. Type '(itself: B) => string' is not assignable to type '(itself: A) => string'. Types of parameters 'itself' and 'itself' are incompatible. Property 'additionalProperty' is missing in type 'A' but required in type 'B'.(2344) input.tsx(6, 2): 'additionalProperty' is declared here.
But B
extends A
. They should be compatible.
UPDATE #1:
I can't explain this, but it seems that if I replace interfaces by classes typing works perfect.
UPDATE #2:
Well, it works only with class methods, but it doesn't work, for example, arrow functions. Still strange.
UPDATE #3
If the interface is defined in the following way it doesn't work:
interface A {
method: (itself: this) => string;
}
But if it's defined in the following way it does work:
interface A {
method(itself: this): string;
}
It has no sense at all. But looking for the reason of this behavior I found this excellent answer. It gave me a clue to reasons of this difference.
There was mentioned the TypeScript option strictFunctionTypes
.
When enabled, this flag causes functions parameters to be checked more correctly.
During development of this feature, we discovered a large number of inherently unsafe class hierarchies, including some in the DOM. Because of this, the setting only applies to functions written in function syntax, not to those in method syntax
It explains the reason of that strange difference in behavior. I can just turn off this option, but it feels like a workaround.
I still need another solution.
UPDATE #4
I assume this error is designated to prevent such unsafe assignments:
const a: A = {
method(b: B) {
return `${b.toString()} / ${b.additionalProperty}`;
}
}
But such type of errors are not specific for my case.
UPDATE #5
I've found another workaround
type Method<T extends (...args: any) => any> = {
f(...args: Parameters<T>): ReturnType<T>;
}['f'];
interface A {
method: Method<(itself: this) => string>;
}
interface B extends A {
additionalProperty: boolean;
}
type Extend<T extends A> = T & { extension: number };
type ExtendedA = Extend<A>
type ExtendedB = Extend<B>
Check it yourself. It's better than disabling of strictFunctionTypes
, but it's still workaround.