TypeScript如何标识数据请求完成后不再为undefined的类型?
TypeScript 可空类型运行时保证的解决方案
以下是针对前置加载完成后、可空类型自动收窄需求的成熟方案,覆盖TS语言特性到Redux工程实践的不同维度:
1. 断言函数做全局前置校验
TS 3.7 引入的断言函数特性,可以标记某个函数执行完成后,对应变量一定符合指定类型,非常适配全局前置请求完成的场景:
// 定义断言函数,校验Redux中对应状态是否已经加载 function assertUserInfoLoaded(): asserts typeof store.getState().user.info is NonNullable<typeof store.getState().user.info> { if (store.getState().user.info === undefined) { throw new Error('访问业务逻辑前未加载用户信息') } } // 在外层API请求完成、进入业务模块前统一调用一次 assertUserInfoLoaded() // 后续所有业务代码中调用对应selector,返回值自动被TS识别为非空 const userInfo = selectUserInfo(store.getState()) // 类型为UserInfo,无undefined
该方案不需要修改原有selector,仅做一次全局校验,后续整个作用域的类型都会自动收窄,同时运行时的报错也能提前拦截非法访问的情况。
2. 状态加加载标记 + 带类型守卫的selector
改造Redux的State结构,新增加载状态标记,通过联合类型实现状态和数据的类型绑定:
// 改造后的State定义,用联合类型关联loaded标记和data的可空性 type UserState = | { loaded: false; data: undefined } | { loaded: true; data: UserInfo } // 对应selector示例 const selectUserState = (state: RootState): UserState => state.user; const selectUserInfo = (state: RootState): UserInfo | undefined => state.user.data; // 使用时通过loaded标记做类型守卫,自动收窄data类型 if (selectUserState(state).loaded) { const userInfo = selectUserInfo(state) // 自动识别为非空的UserInfo类型 }
如果业务逻辑中可以确定状态已经加载完成,也可以直接衍生出返回非空类型的selector,不需要维护多套独立的selector逻辑。
3. 作用域封装收拢判断逻辑
工程实践中最常用的方案是将需要依赖该数据的业务逻辑,全部收拢到内层作用域,外层统一做加载判断,内层默认类型非空:
// React场景下的外层守卫组件示例 function RequireUserLoaded({ children }: { children: React.ReactNode }) { const userState = useSelector(selectUserState) if (!userState.loaded) return <LoadingPage /> return <>{children}</> } // 所有被该组件包裹的业务组件,默认用户信息已经加载完成,可以直接使用返回非空类型的selector const selectRequiredUserInfo = (state: RootState): UserInfo => state.user.data as UserInfo
该方案将状态校验逻辑收拢到外层,内层业务代码不需要额外处理空值情况,类型定义也可以直接写为非空,维护成本最低。
极简临时方案
如果是小项目、且可以100%保证调用顺序不会出错,可以直接使用TS非空断言操作符!,示例:const userInfo = selectUserInfo(state)!,仅适合少数调用场景使用,不建议大面积铺开。
内容的提问来源于stack exchange,提问作者uloco
相关产品推荐
相关产品推荐

