Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type annotation for `this` keyword in Typescript

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?

like image 705
AndrewE Avatar asked Jul 21 '17 16:07

AndrewE


People also ask

How do I use this keyword in TypeScript?

The "this" keyword always points to the object that is calling a particular method. The type of "this" in an expression depends on the location in which the reference occurs: In a constructor, member function, or member accessor, this is of the class instance type of the containing class.

How do you annotate types in TypeScript?

TypeScript - Type Annotations We can specify the type using :Type after the name of the variable, parameter or property. There can be a space after the colon. TypeScript includes all the primitive types of JavaScript- number, string and boolean. In the above example, each variable is declared with their data type.

What is type keyword in TypeScript?

Type keyword in typescript: In typescript the type keyword defines an alias to a type. We can also use the type keyword to define user defined types.

How do you type TypeScript assert?

However, there is another way to do type assertion, using the 'as' syntax. let code: any = 123; let employeeCode = code as number; Both the syntaxes are equivalent and we can use any of these type assertions syntaxes.


1 Answers

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!

Playground link to code


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!
like image 131
jcalz Avatar answered Sep 17 '22 19:09

jcalz