Reputation: 723
on "this" in the following TypeScript code I get the following error:
Argument of type 'this' is not assignable to parameter of type 'T'. Type 'MyClass' is not assignable to type 'T'.ts(2345)
written in TypeScript 3.2.2 (in Angular 7)
export abstract class MyClass<T extends MyClass<T>> {
childList: T[];
foo = () => {
const index = this.childList.indexOf(this);
// ...
}
}
This is how I would write it in C#:
public abstract class MyClass<T> where T : MyClass<T>
{
public T[] childList { get; set; }
public void foo()
{
int index = Array.IndexOf(childList, this);
}
}
Thanks a lot for any feedback !
Upvotes: 1
Views: 42
Reputation: 329418
I'm not sure about the C#, but in TypeScript you are declaring that T extends MyClass<T>
, which means that T
is a subtype of MyClass<T>
, not identical to MyClass<T>
. So any value of type T
can be assigned to a variable of type MyClass<T>
, but not vice versa.
The type definition of Array<X>.indexOf(y)
expects y
to be assignable to X
, not vice versa. So it is an error to call Array<T>.indexOf()
on a parameter of type MyClass<T>
. One way to address this is to declare childList
to be of type Array<MyClass<T>>
instead of Array<T>
:
childList!: MyClass<T>[];
But for all I know that would show up as an issue somewhere else, because at some point you will want to assume that T
is identical to MyClass<T>
, and it just isn't the case. You can always fix things with type assertions, but I think that you might want a completely different solution here: polymorphic this
.
Instead of trying to represent a circular/recursive type like Foo<T extends Foo<T>>
and chasing your own tail, use the type this
to represent "the type of the current class". Your MyClass
becomes:
export abstract class MyClass {
childList!: this[];
foo() {
const index = this.childList.indexOf(this); // okay
}
}
And subclasses more or less work the way you expect:
export class MyConcreteClass extends MyClass {
bar() {
this.childList.indexOf(this); // still works
}
}
And instances cash out this
into the actual type of the instance:
const y = new MyConcreteClass();
y.childList; // type MyConcreteClass[];
Hope that helps; good luck!
Upvotes: 3