Неопределенный тип объекта TypeScript все портит

#reactjs #typescript

#reactjs #typescript

Вопрос:

Я часто сталкиваюсь с ситуацией, когда я сохраняю объект в состоянии реакции, например, пользователя:

 ...
user: undefined,
setUser: (user) => set((state) => ({ user: { ...user, ...state.user } as User })),
  

Я определяю тип TypeScript как

 export type User = {
  verified: boolean;
  firstName: string;
  lastName: string;
  userType: 'user' | 'admin';
};
  

Итак, на данный момент TypeScript будет жаловаться на то, что user , который я создаю как User тип, является undefined , хотя этого не может быть. ОТЛИЧНО! Поэтому я добавляю | undefined в конце User определения типа. Итак, это позаботится об этом конкретном файле, но затем я начинаю получать миллион и одно другое предупреждение повсюду, потому что, например, я вызываю user.firstName , но теперь, когда пользователь может быть неопределенным, я действительно должен вызвать user?.Имя пользователя.
Добавление условий, таких как if(user) , похоже, не удовлетворяет TS. В итоге я трачу три часа на исправление ошибок для TS, только чтобы узнать, что мой код теперь ломается из-за этого недавнего изменения.
Это очень похоже на попытку подавить пожары, которые я сам запустил, когда использовал TypeScript. Разочарование.

Может ли кто-нибудь, кто хорошо знает TS, пожалуйста, предложить некоторые рекомендации? Как бы вы подошли к этому? Есть ли способ обрабатывать такие «неопределенные» случаи?

Ответ №1:

Предполагая, что вы определили пользователя как:

 const user: User|undefined = ...;
  

Если вы хотите получить доступ к свойству пользователя, используйте этот синтаксис:

 user!.firstName
  

Это говорит typescript, что вы знаете, что делаете, и это user никогда не является неопределенным или нулевым. Он также сообщает typescript, что свойства, к которым обращаются таким образом, не должны предполагаться, что они могут быть null или undefined , и я предполагаю, что именно поэтому вы теперь получали больше ошибок.


Внимание!

 const user: User|undefined = ...;
  

избыточный ввод. Вы можете просто вернуться к использованию

 const user: User = ...;
  

но вместо того, чтобы использовать ?. оператор для доступа к свойствам, продолжайте использовать !. .

Извините, если вы впервые используете typescript. Обычно все не так плохо.

Комментарии:

1. Полезный совет по поводу восклицательного знака. Забыл об этом.

Ответ №2:

Рассмотрите возможность разделения ваших компонентов на:

  • Компоненты, у которых ДОЛЖЕН быть пользователь
  • Компоненты, у которых МОЖЕТ быть пользователь

Из компонента, у которого может быть пользователь, отобразите компонент, у которого он должен быть.

 class UserComponent extends React.Component<{ user: User }> {
  render() {
        // Note: since user must be defined for this component, we are able to access properties without the ? or ! syntax
        return (
            <div>{this.props.user.firstName}</div>
        )
    }
}

// Note: this.state.user is optional, and therefore is initialized as undefined
class MayHaveUserComponent extends React.Component<{}, { user?: User }> {
    render() {
        if (this.state.user) {
            return <UserComponent user={this.state.user} />
        }
        return (
            <div>No user yet!</div>
        )
    }

    setUser(user: User) {
      this.setState({ user: { ...user, ...this.state.user } })
    }
}
  

Комментарии:

1. Интересная идея, но не сработает для моего потока приложений.

Ответ №3:

Вы не просто делаете typescript счастливым здесь. Если вы переходите user: undefined к коду, который ожидает, что будет объект user, то это определенно вызовет ошибки во время выполнения при использовании undefined like an object. Эти проверки необходимы, если это должно быть разрешено.

Обычно я справляюсь с этим в react, возвращая ранний рендеринг, если требуемое значение равно null или undefined . После этого возврата вы можете предположить, что пользователь существует как обычно.

 class Component({ user: User | null | undefined }) {
  if (!user) return <SomeLoadingIndicator /> // or: return null, or whatever

  // from here on, `user` is known to be of type `User`, not null, and not undefined.

  return <div>{user.lastName}</div>
}
  

Добавление условий, таких как if(user) , похоже, не удовлетворяет TS.

Это действительно так. Вам просто нужно быть в условной ветке, где была запущена проверка. Это означает, что вы должны быть в { } инструкции if:

 const user: User | null = Math.random() > 0.5 ? getUser() : null
if (user) {
  // here user is of type: User
} else {
  // here user is of type: null
}
// here user is of type: User | null
  

Или в коде, который мог выполняться только в том случае, если условие было правдивым.

 const user: User | null = Math.random() > 0.5 ? getUser() : null
if (!user) {
  // here user is of type: null
  throw new Error("user is required!")
}
// here user is of type: User
  

Комментарии:

1. Вы правы в том, что TS соблюдает условие. Это была ошибка с моей стороны. Здесь есть хорошие моменты.