-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Polymorphic this[K]
inside a class
#41495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
this[K]
inside a child classthis[K]
inside a class
If you write code inside the class in terms of class B extends A {
public test: number = 0;
public obj: object = { };
public bar() {
var isThis = this.clone();
isThis.test = 2; // WORKS, since `this` is resolved to `B`
// Violates C's contract
this.change({ test: 10 });
// Violates C's contract
this.onChange(["obj"]);
}
}
// Legal extension of B
class C extends B {
test: 5 = 5;
obj: Function = () => { };
} Once you're outside the class, you're allowed to assume you're not aliasing and get back types resolved against the type of the reference (since otherwise you'd never be able to do anything ever). |
You argument has some merits; however I dont think it explain the baheier fully. At least it didn't to me. Consider this: type NonFunctionPropertyNames<T> = {
// tslint:disable-next-line:ban-types
[K in keyof T]: T[K] extends Function ? never : K;
}[keyof T];
class A {
public change(map: Partial<this>) {
// do something
}
public onChange(fields: Array<NonFunctionPropertyNames<this>>) {
// do something
}
public clone(): this {
return this;
}
}
class B extends A {
public test: number = 0;
public obj: object = { };
public bar() {
var isThis = this.clone();
isThis.test = 2; // WORKS, since `this` is resolved to `B`; but should it? since it's type might be changed later
this.change({ test: 10 }); // ERROR, can't assign number to type `this["test"]`
this.onChange(["test"]); // ERROR, can't validate "test" as a valid property name
}
}
class C extends B {
public test: 5 = 5;
public obj: Function = () => { };
}
const b = new B();
b.change({ test: 10 }); // works as it should!!
b.onChange(["test"]); // works as it should!!
const c: B = new C();
c.change({ test: 10 }); // works; but it shouldn't if we follow the same logic
c.onChange(["test"]); // works as it should!! There are following problems if I accept the reason provided here for this behavior:
Breaking a subclass should be a developer's choice, and they should accept the responsibility of doing so. If it was up to me, I would not even allow that, but this is JS at the end of the day, so it's understandable. But now it is affecting a very legal and common use of So I understand if you can't fix 1 and I don't expect anything to change with 3 since we are explicitly casting the object to |
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
The current behavior arises from a set of consciously-chosen trade-offs. We definitely could have made other trade-offs, as you describe, but we didn't - that isn't a bug, just a difference in opinion. |
Essentially it seems that the typescript can not resolve the type of a property when it is trying to do a
[K in keyof T]: T[K]
operation. I don't know the name of this. lookup? in any case,T[K]
forT = this
is alwaysthis[K]
and is never resolved further inside of the class. It works great when used from the outside of the class.TypeScript Version: 4.1.0-dev.20201101
Search Terms: "Polymorphic this" "this generic"
Code
Consider the following code:
Expected behavior:
For it to work regardless of the code being inside the class or outside of it.
Actual behavior:
See code.
Playground Link: typescriptlang.org with inheritance, my actual problem
Related Issues: This issue might have something to do with #39204 or #22934.
The text was updated successfully, but these errors were encountered: