Свойство ‘id’ не существует для типа аргумента функции

#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 интерфейса…