I would like to be able to reference the type of the current object in a function's parameter/return type, such as the following:
in .d.ts
:
interface Object {
add(object: Partial<this>): this;
}
in .js
:
Object.prototype.add = function(object) {
Object.assign(this, object);
}
The intended behavior of this function is to only allow the parameter to contain properties that are already defined on the object via Partial<this>
. However, in attempted use such as
document.body.style.add({
fontSize: "12px"
});
TypeScript gives me the following error:
Argument of type '{ fontSize: string; }' is not assignable to parameter of type `Partial<Object>`.
Object literal may only specify known properties, and 'fontSize' does not exist in type 'Partial<Object>'.
I assume that this is because the this
types in my .d.ts
actually just refers to Object
because that's the containing interface for the function instead of recognizing the CSSStyleDeclaration
type that document.body.style
is. How can I achieve a function like this, that uses the type of the object it's being applied to as parameters/return type? Is this possible in TypeScript?
CodePudding user response:
Disclaimer: it is considered bad practice to modify native prototypes this way. You may have some reason why it's okay to do this for your use cases, but you should probably avoid it. Anyway, let's move on.
As you noticed, the polymorphic this
type doesn't really work the way you want for the native interface
types like Object
, String
, etc.. Even known subtypes of Object
will see this
rendered as Object
instead of the known subtype.
As a workaround you can emulate the desired behavior by making the method generic and give it a generic this
parameter:
interface Object {
add<T>(this: T, object: Partial<T>): T;
}
So now if you call add()
on some object of type CSSStyleDeclaration
, the type checker will infer T
to be CSSStyleDeclaration
, and thus expect a Partial<CSSStyleDeclaration>
as its parameter. So the following works as expected:
document.body.style.add({
fontSize: "12px"
});
// (method) Object.add<CSSStyleDeclaration>(
// this: CSSStyleDeclaration, object: Partial<CSSStyleDeclaration>
// ): CSSStyleDeclaration