I'm reading Declaring this
in a Function from the TypeScript Handbook, and I'm confused by this example:
... but there are a lot of cases where you need more control over what object
this
represents. The JavaScript specification states that you cannot have a parameter calledthis
, and so TypeScript uses that syntax space to let you declare the type forthis
in the function body. This pattern is common with callback-style APIs, where another object typically controls when your function is called.interface DB { filterUsers(filter: (this: User) => boolean): User[]; } const db = getDB(); const admins = db.filterUsers(function (this: User) { return this.admin; });
I didn't fully get this paragraph message, in particular how filterUsers
method should be implemented so that explicit this: User
would work, so I decided to bring this example to a working state:
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db:DB = {
filterUsers(filter: (this: User) => boolean) {
return users.filter(filter);
}
};
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
console.log(admins);
Obviously this example won't work, although the compiler doesn't complain. It's even more clear from the resulting JavaScript - this
will not be User
instance inside function (this: User) { return this.admin; }
:
"use strict";
const users = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db = {
filterUsers(filter) {
return users.filter(filter);
}
};
const admins = db.filterUsers(function () {
return this.admin;
});
console.log(admins);
So, what would be the correct implementation filterUsers
so that the idea of explicit this: User
would work and be useful?
I would recommend the filter function that takes a User object as an argument instead of relying on this:
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (user: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db: DB = {
filterUsers(filter: (user: User) => boolean) {
return users.filter(filter);
}
};
const admins = db.filterUsers((user: User) => {
return user.admin;
});
console.log(admins);
If you want to try to set the param name to be this, You could also use filter.call(user) inside the filterUsers method, or explicitly bind the object like @rozsazoltan did.
This ensures that this inside the filter function refers to the current user object.
type User = {
id: number;
admin: boolean;
}
interface DB {
filterUsers(filter: (this: User) => boolean): User[];
}
const users: User[] = [
{ id: 1, admin: true },
{ id: 2, admin: false },
];
const db: DB = {
filterUsers(filter: (this: User) => boolean) {
return users.filter(user => filter.call(user));
}
};
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
console.log(admins);