Reputation: 353
I have a standalone function that's meant to use the context provided by Function.prototype.call
.
For example:
function foo () {
return this.bar;
}
> foo.call({bar: "baz"})
baz
Is there a way to provide a Typescript type annotation for the this
keyword in this scenario?
Upvotes: 21
Views: 12787
Reputation: 330216
First of all, you can use the special this
parameter syntax to identify the type of object you expect this
to be:
function foo (this: {bar: string}) {
return this.bar; // no more error
}
which helps if you call it directly:
foo(); // error, this is undefined, not {bar: string}
var barHaver = { bar: "hello", doFoo: foo };
barHaver.doFoo(); // acceptable, since barHaver.bar is a string
var carHaver = { car: "hello", doFoo: foo };
carHaver.doFoo(); // unacceptable, carHaver.bar is undefined
UPDATE for TS3.2+
TypeScript 3.2 introduced the --strictBindCallApply
compiler option which strongly types the .call() method of functions. If you use this (or the --strict
suite of compiler features which includes this), then the this
parameter will also enforce that foo.call()
behave as desired:
foo.call({ bar: "baz" }); // okay
foo.call({ baz: "quux" }); // error!
pre TS3.2 answer follows:
But you want to use foo.call()
. Unfortunately the Function.prototype.call()
typing in TypeScript won't really enforce this restriction for you:
foo.call({ bar: "baz" }); // okay, but
foo.call({ baz: "quux" }); // no error, too bad!
Merging something better into TypeScript's Function
declaration caused me problems, (First point of ugliness; you will need to cast foo
to something) so you can try something like this:
interface ThisFunction<T extends {} = {}, R extends any = any, A extends any = any> {
(this: T, ...args: A[]): R;
call(thisArg: T, ...args: A[]): R;
}
A ThisFunction<T,R,A>
is a function with a this
of type T
, a return value of type R
, and a rest argument of type A[]
. (Second point of ugliness: you can't easily specify multiple arguments of different types in a way that will be enforced by the type system.)
You can then cast foo
to ThisFunction<{ bar: string }, string>
, (Third point of ugliness: the type system just will not infer this
types) and then finally use call()
:
(<ThisFunction<{ bar: string }, string>>foo).call({ bar: "baz" }); // okay, and
(<ThisFunction<{ bar: string }, string>>foo).call({ baz: "quux" }); // error, hooray!
Upvotes: 32