网站搜索

javascript - 如何向 TS 表示 store 中的值即使是可选的也有值?


我有以下情况:

我有一家包含可选商品的标准商店。

我还有一个依赖于该存储的元素树。我还在多个组件中选择 {account}。

对于业务逻辑,我必须在最顶层检查账户是否已设置。如果没有设置,我就不渲染依赖于它的组件。

我怎样才能告诉 TS,尽管这个值在存储中是可选的,但我 100% 确定它不是 undefined?

示例代码:

// store
interface Account {
  id: number;
  name: string;
}

export interface AppState {
  account?: Account;
}

const initialState: AppState = {};

const accountSlice = createSlice({
  name: "account",
  initialState,
  reducers: {
    setAccount(state: AppState, action: PayloadAction<Account | undefined>) {
      state.account = action.payload;
    }
  }
});

// component
const GrandChild = () => {
  const { account } = useSelector((state: RootState) => state, shallowEqual);
  return <>{account.name}</>;
};

const Child = () => {
  const { account } = useSelector((state: RootState) => state, shallowEqual);
  return account ? <GrandChild /> : <>account not set</>;
};

export default function App() {
  const dispatch = useDispatch();
  useEffect(() => {
    // dispatch(setAccount({ id: 0, name: "john" }));
  });
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <Child />
    </div>
  );
}

Codesandbox :

https://codesandbox.io/s/required-reducer-ysts49?file=/src/App.tsx

我知道我可以做到这一点:

const { account } = useSelector((state: RootState) => state, shallowEqual) as Account;

但这看起来很临时。有更好的方法吗?

答案#1

你知道,如果 account 未定义,Grandchild 是不会被渲染的。那么为什么不把 account 作为属性传递给 Grandchild 组件呢?这样你就可以定义这个属性永远不会是未定义的。而且由于你只在确认 account 不为未定义之后才渲染 Grandchild,你应该可以把 account 作为属性传递给组件(并且由于你定义了这个属性不会是未定义的,TS 就不会对你在 Grandchild 组件中使用 account.name 提出异议)。

不过我不懂 redux——我从未使用过它,也不了解它,所以我不知道这个答案是否与它兼容,或者 redux 是否会引起我无法预见的一些问题。

我写了一点代码,展示这可能是什么样子(但正如我已经说的,我不知道如何使用 redux,所以你可能需要接受我的想法并编写代码以使一切正常工作)——所以我的代码示例可能更多是对我意思的一种可视化,而不是解决方案。

const GrandChild = (account: Account) => {
  return <>{account.name}</>;
};

const Child = () => {
  const { account } = useSelector((state: RootState) => state, shallowEqual);
  return account ? <GrandChild account={account} /> : <>account not set</>;
};