对来自React.useContext(或其他任何地方)的值断言非null时的最佳实践



在我的React应用程序中,我使用非null断言(!(来告诉TS来自useContext钩子的值不是null。它运行良好,似乎是非null断言的教科书用例,但根据推荐的esint规则,非null断言是一个警告。

作为TypeScript的新手,我想确保在处理这些情况时不会错过一个常见的模式或最佳实践方法。或者,在下面的示例中,可能有不同的方法来键入user对象。

  1. 状态是在一个独立文件中定义的
// context.ts
interface State {
user: User | null; // User type is defined elsewhere, has 'id' and 'email'
}
const state: State = {
user: null,
};
  1. 如果用户已在定义中登录state.user
// App.tsx
return state.user ? <Dashboard /> : <Login />;
  1. 只有当state.user不为空时,用户才能看到仪表板,但我仍然看到以下错误
// Dashboard.tsx
const { state } = React.useContext(context);
const name = state.user.firstName; // Object is possibly 'null'.
const { id, email } = state.user; // Property 'id/email' does not exist on type 'User | null'.
  1. Logout方法设置state = { user: null },因此null必须可分配给User类型

预期的潜在答案无法解决我关于最佳实践/模式的问题:

  • 我知道如何禁用@typescript-eslint/no-non-null-assertion规则,这不是我想要的解决方案。

  • 我知道,如果在第一个示例中使用可选链接(如const name = state.user?.firstName;(,我可以避免错误和非null断言警告,但这并不能回答我的问题。在这种情况下,我实际上更喜欢非null断言,因为它会更明确。

谢谢!

有几个选项可供选择:

  1. 明确检查user不是null,如果是则抛出

  2. 定义一个";登录";状态接口,并编写一个钩子,在检查user不是null后返回

  3. 定义一个";登录";状态接口并编写一个类型断言函数,然后在Dashboard或钩子中使用

它们之间的共同主题不仅仅是断言user不是null,而是在运行时证明它,以便主动、清晰地捕捉使用statenull的编程错误,而不是看起来随机的";不能使用CCD_ 18的X";错误。

1.明确检查

由于用户只有在登录时才能看到Dashboard组件,并且当他们在user中登录时,上下文成员不会是null,因此可以显式检查:

const { state } = React.useContext(context);
const { user } = state;
if (!user) {
throw new Error(`ERROR: User reached Dashboard with null 'user' in context`);
}
// ...code uses `user`...

这样,TypeScript就知道组件代码的其余部分user不是null,如果在usernull时编程错误导致尝试使用Dashboard,则会得到一个明显的错误。

2.记录状态和挂钩

或者,您可以定义LoggedInState:

interface LoggedInState {
user: User;
}

并编写一个可重复使用的钩子:

function useLoggedInContext(context: ContextType): LoggedInState {
const { state } = React.useContext(context);
if (!state.user) {
throw new Error(`ERROR: User reached logged-in-only component with null 'user' in context`);
}
return state as LoggedInState;
}

它仍然有一个类型断言,但在一个可重用的位置,上面的运行时代码证明它是一个正确的断言。

然后在Dashboard(和其他地方(中:

const { state } = useLoggedIncontext(context);
// ...

3.一个类型断言函数

您可以将其封装在类型断言函数中:

function assertIsLoggedInState(state: State): asserts state is LoggedInState {
if (!state.user) {
throw new AssertionError(`'state' has a null 'user' member, expected a logged-in state`);
}
}

然后直接在Dashboard:中使用

const { state } = React.useContext(context);
assertIsLoggedInState(state);
// ...code can use `state.user`, which TypeScript now knows can't be `null`

或者在useLoggedInContext挂钩中使用:

function useLoggedInContext(context: ContextType): LoggedInState {
const { state } = React.useContext(context);
assertIsLoggedInState(state);
return state; // TypeScript knows this is a LoggedInState
}

作为对第一个语句的一个小调整:如果你不需要state来做任何其他事情,你可以将前两个语句组合起来:

const { state: { user } } = React.useContext(context);
// ...

它只声明了user,而没有声明state。如果您在state中有其他需要的东西,您可以将它们添加到user之后。

最新更新