#typescript #vue.js #vuejs3
Вопрос:
Я написал отправку, которая разрешает пользователю, эта отправка возвращает один из типов: User или responseError
Моя депеша
async loginUser ({ commit }, data) {
try {
const user = await loginUser(data)
commit('setUser', user)
return user
} catch (e) {
return {
status: e.response.status,
errors: e.response.data.errors
}
}
}
Типы
export interface User {
id?: number
}
export interface ErrorResponse {
status: number
errors: object
}
export interface Actions {
loginUser({ commit }: { commit: Commit }, data: LoginData): Promise<User | ErrorResponse>
}
И я называю это действие в своем компоненте так:
const res = await this.$store.dispatch('loginUser', {
email: this.formData.email,
password: this.formData.password
})
redirect(res, 'dashboard')
После запроса я пытаюсь проверить, что возвращается в запросе (в функции перенаправления): Ошибка или пользователь
const redirect = (res: User | ErrorResponse, redirectTo: string) => {
if (res.id) {
router.push({ name: redirectTo })
} else {
ElMessage.error('Oops, this is a error message.')
}
}
Но ТС покажи мне ошибку
Property 'id' does not exist on type 'User | ErrorResponse'.Property 'id' does not exist on type 'ErrorResponse'.
Я понимаю, почему TS показывает мне эту ошибку, но я не понимаю, как я могу ее исправить ?
Ответ №1:
Проблема res
может быть либо в User
интерфейсе, либо в ErrorResponse
интерфейсе, и TypeScript нуждается в подсказке для устранения неоднозначности. Есть несколько способов решить эту проблему.
Используйте утверждение типа
Быстрое решение заключается в использовании утверждения типа с as
ключевым словом:
const redirect = (res: User | ErrorResponse, redirectTo: string) => {
if ((res as User).id) {
// res is User or ErrorResponse
console.log('User ID', (res as User).id)
} else {
//...
}
}
Недостатком этого подхода является то, что тип по res
-прежнему неоднозначен в if
блоке -, поэтому для доступа к определенному свойству по-прежнему требуется утверждение типа (или временная переменная типа User
, на которую ссылается res
) User
.
Вместо этого мы можем использовать сужение типов для дополнительной безопасности типов, как показано ниже. Примечание. Хотя typeof
защита типов или instanceof
является способом сужения типов, их нельзя использовать с интерфейсами или псевдонимами типов.
Используйте in
защиту типа
Используйте in
оператор для проверки взаимоисключающего свойства в одном из типов, тем самым сужаясь до типа, который проходит проверку. Например, id
является уникальным для User
, поэтому убедитесь, что свойство является in
res
объектом:
const redirect = (res: User | ErrorResponse, redirectTo: string) => {
if ('id' in res amp;amp; res.id) {
// res is User
console.log('User ID', res.id)
} else {
//...
}
}
Используйте функцию защиты типа с предикатом типа
Используйте функцию с предикатом типа, чтобы убедиться, что данный объект относится к определенному типу. Поскольку id
это уникально User
, функции просто необходимо проверить, что свойство не является undefined
:
const isUser = (obj: any): obj is User => (obj as User).id !== undefined
const redirect = (res: User | ErrorResponse, redirectTo: string) => {
if (isUser(res)) {
// res is User
console.log('User ID', res.id)
} else {
//...
}
}
Комментарии:
1. Спасибо! Это очень хороший ответ.
2. Действительно, очень хороший ответ. Я все еще озадачен тем, почему простого
if(res.id)
недостаточно, чтобы сузить типUser
интерфейса…