We want to create a generic method on a superclass that accepts an argument that is the name of any property on the subclass that is of a certain type. Consider this (very contrived) example:
class CanUpcase {
upcase<PropertyName extends keyof this>(
propertyName: this[PropertyName] extends string ? PropertyName : never
) {
return this[propertyName].toUpperCase(); // Property 'toUpperCase' does not exist on type 'this[this[PropertyName] extends string ? PropertyName : never]'.
}
}
class User extends CanUpcase {
age = NaN;
name = ``;
constructor() {
super();
this.upcase('name'); // Argument of type '"name"' is not assignable to parameter of type 'this["name"] extends string ? "name" : never'.ts(2345)
}
}
const user = new User();
user.upcase('name'); // No error
We expected that upcase
would accept 'name'
, since the 'name'
property is a string.
However, while user.upcase('name')
works as expected, an error is thrown by this.upcase('name')
as shown.
Plus, within upcase
, this[propertyName]
is not recognized as a string.
Why does
this.upcase
throw an error and notuser.upcase
?Why isn't
this[propertyName]
recognized as a string?
Thanks!
We want to create a generic method on a superclass that accepts an argument that is the name of any property on the subclass that is of a certain type. Consider this (very contrived) example:
class CanUpcase {
upcase<PropertyName extends keyof this>(
propertyName: this[PropertyName] extends string ? PropertyName : never
) {
return this[propertyName].toUpperCase(); // Property 'toUpperCase' does not exist on type 'this[this[PropertyName] extends string ? PropertyName : never]'.
}
}
class User extends CanUpcase {
age = NaN;
name = ``;
constructor() {
super();
this.upcase('name'); // Argument of type '"name"' is not assignable to parameter of type 'this["name"] extends string ? "name" : never'.ts(2345)
}
}
const user = new User();
user.upcase('name'); // No error
We expected that upcase
would accept 'name'
, since the 'name'
property is a string.
However, while user.upcase('name')
works as expected, an error is thrown by this.upcase('name')
as shown.
Plus, within upcase
, this[propertyName]
is not recognized as a string.
Why does
this.upcase
throw an error and notuser.upcase
?Why isn't
this[propertyName]
recognized as a string?
Thanks!
Share Improve this question asked Mar 2 at 6:31 RobertAKARobinRobertAKARobin 4,2844 gold badges29 silver badges47 bronze badges1 Answer
Reset to default 2You make the child property available in a superclass with a generic parameter:
Playground
type KeysOfType<T extends object, V> = keyof {[K in keyof T as T[K] extends V ? K : never]: unknown};
class CanUpcase<T extends object> {
upcase(propertyName: KeysOfType<T, string>) {
return (this[propertyName] as string).toUpperCase();
}
}
class User extends CanUpcase<User> {
age = NaN;
name = ``;
constructor() {
super();
this.upcase('name'); // ok
}
}
const user = new User();
user.upcase('name'); // No error
Autocompletion works also: