我该如何修复 "Cannot invoke an object which is possibly 'undefined'" 错误?

How do i fix "Cannot invoke an object which is possibly 'undefined'" error?

我在 Typescript 中使用 CreateContext,我明白为什么代码有问题,但我不知道如何解决它。它是类型安全 TX 的基本用法,使状态和 (useReducer) 分派在组件层次结构中可用

沙盒: https://codesandbox.io/s/typescript-usereducer-todo-lk391?file=/src/App.tsx

上下文接口:

export interface ContextInterface {
  state: AppState;
  dispatch: (action: ActionType) => void;
}

使用 createContext 接口(我认为它必须是部分的,因为 createContext 不会采用零参数)

const TodoContext = createContext<Partial<ContextInterface>>({});

上下文已在我的父组件中初始化:

  let [state, dispatch] = useReducer(reducer, initialState);
  ...
  return (
    <TodoContext.Provider value={{ state, dispatch }}>
  ...

当我使用上下文 TS 报告无法调用可能是 'undefined'.ts(2722) 的对象时,但是 console.log 按预期执行。

  let { dispatch } = useContext(TodoContext);
  console.log("dispatch", dispatch);

有没有办法正确定义Context对象并消除错误?

您可以将 useContext 调用包装在一个自定义挂钩中,该挂钩将处理类型问题:

  1. 首先更改 TodoContext 的定义以包含可选的空状态

const TodoContext = createContext<ContextInterface | undefined>(undefined);
  1. 创建一个自定义挂钩,抛出有关如何使用该挂钩的信息性错误
export const useTodoContext = (): ContextInterface => {
  const context = useContext(TodoContext)
  if (!context) {
    throw Error('useTodoContext must be used inside TodoContext.Provider')
  }
  return context
}

那么你就可以毫无问题地使用这个钩子了。

此方法适用于所有类型的上下文数据。

从根本上说,问题是通过使用 Partial,您已将上下文类型的所有属性设为可选,因此它们可能是 undefined(从类型的角度来看),并且您需要如果你走那条路,允许这样做。

根据 createContext documentation,默认情况下 仅在没有相关提供程序时使用 ,因此您可以不使用 Partial,而只包含一个始终抛出的默认上下文对象:

const TodoContext = createContext<ContextInterface>({
    state: {/*...mocked AppState stuff...*/},
    dispatch: (action: ActionType) => {
        throw new Error(`Default context used unexpectedly, check you have a provider`);
    }
});

这样,TypeScript 就不会认为 dispatch 可能是 undefined

如果 AppState 属性没有任何合理的默认值,您可以使用 Proxy 在任何 属性 访问时抛出类似于上述错误的错误,因此尝试使用 context.state.x 会提供同样明显的错误。