Я читаю Объявление этого в функции из TypeScript Handbook, и меня смущает этот пример:
... но во многих случаях вам нужен больший контроль над тем, что объект
this
представляет. Спецификация JavaScript гласит, что вы не может иметь параметр с именемthis
, поэтому TypeScript использует его синтаксическое пространство, позволяющее объявить типthis
в функции тело. Этот шаблон является общим для API в стиле обратного вызова, где другой объект обычно контролирует, когда вызывается ваша функция.interface DB { filterUsers(filter: (this: User) => boolean): User[]; } const db = getDB(); const admins = db.filterUsers(function (this: User) { return this.admin; });
Я не до конца понял суть этого абзаца, в частности, как следует реализовать метод filterUsers
, чтобы явный this: User
работал, поэтому я решил привести этот пример в рабочее состояние:
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.info(admins);
Очевидно, что этот пример не будет работать, хотя компилятор не жалуется. Из полученного JavaScript ещё более понятно — this
не будет экземпляра User
внутри 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.info(admins);
Итак, какой должна быть правильная реализация filterUsers
, чтобы идея явного this: User
работала и была полезной?
Вы не совсем об этом спрашиваете, так что я думаю, это просто отступление, но причина, по которой ваша неудачная попытка не имеет ошибки TS, заключается в отсутствии --strictThis
, как это требуется в ms/TS#7968 . TS всегда позволит вам передать функцию this
-параметра там, где ожидается параметр без this
, как вы видите в своем коде и как минимально продемонстрировано в этой ссылке на игровую площадку. Я не знаю, как вставить эту информацию в свой пост.
Используя привязку, вы можете убедиться, что это действительно правильный экземпляр пользователя во время выполнения функции фильтра.
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) {
// need to manually bind the `this` context to each user
return users.filter(user => filter.bind(user)());
}
};
const admins = db.filterUsers(function (this: User) {
return this.admin;
});
console.info(admins);
Может показаться, что вы не можете контролировать значение
this
, поскольку его значение устанавливается автоматически, но на самом деле это не так.У каждой функции есть метод
.bind
, который возвращает новую функцию,this
привязанную к значению. Функция ведет себя точно так же, как и та, которую вы вызвали.bind
, только онаthis
была установлена вами. Независимо от того, как и когда вызывается эта функция,this
всегда будет ссылаться на переданное значение.Источник: Как получить правильный this внутри обратного вызова
Феликс Клинг
Я бы порекомендовал функцию фильтра, которая принимает объект User в качестве аргумента вместо того, чтобы полагаться на это:
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.info(admins);
Если вы хотите попытаться установить такое имя параметра, вы также можете использовать filter.call(user) внутри метода filterUsers или явно привязать объект, как это сделал @rozsazoltan.
Это гарантирует, что это внутри функции фильтра относится к текущему объекту пользователя.
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.info(admins);
Итак, в этом примере из книги нет никакого практического удобства в использовании явного this
, так как было бы понятнее написать его обычным способом? function (user: User) { return user.admin; }
@Serg, ответ был обновлен, я только что заметил, что вы хотите использовать это имя в качестве имени параметра, что, я не думаю, имеет какое-либо практическое удобство.
@Serg, это мое личное мнение, я бы всегда рекомендовал использовать user:User вместо этого:User, чтобы избежать путаницы.
Моя вина была в том, что я пытался на их примере увидеть, что явный this
помогает избежать какой-то ошибки или дает некоторое удобство при написании кода. Но тот пример из книги оказывается надуманным.
@Serg Обновите ответ, включив в него оба, я по-прежнему предпочитаю передавать объект пользователя для ясности и простоты :)
Я думаю, вы допустили опечатку, потому что оба примера теперь идентичны. Но я абсолютно согласен — использование this: User
только излишне усложняет код в этом контексте.
@Serg Да, моя ошибка. Ответ обновлен, спасибо!
@Serg, не могли бы вы также проголосовать за этот ответ?
«в частности, как следует реализовать метод filterUsers, чтобы явно указать следующее: User будет работать», функцию необходимо вызывать с контекстом
User
. Вероятно, через.apply()
/.call()
/.bind()
.