TypeScript将空值检查赋值为函数作用域复用常量避免非空断言
问题解答
TypeScript 默认的控制流分析不会通过提前存储的布尔值常量(比如你写的isStartSelected、isEndSelected)反向收窄原变量interval的类型。TS的类型收窄只会跟踪直接作用在变量上的类型守卫判断,不会自动关联「存储了判空结果的中间常量」和「原对象属性」之间的类型关系,所以你直接复用这两个布尔常量的话,还是会遇到interval.start可能为空的类型报错。
不需要重复写判空逻辑、也不用非空断言的最优写法是:不要把判空结果存成布尔值,直接把可能为空的属性值提取为常量,TS会对这些常量本身做控制流收窄,后续直接用收窄后的常量即可,完全不需要再访问interval上的对应属性。
优化后的代码如下:
const [interval, setInterval] = useState<PartialInterval | null>(null); const handleDaySelect = (day: DateData) => { // 直接提取属性值,不存布尔判断结果 const start = interval?.start; const end = interval?.end; if (!start) { setInterval({ start: parseDayToDate(day.dateString) }); return; } // 走到此处时start已经被TS收窄为非空类型,无需重复判断start存在,也不用访问interval.start if (!end) { const isSelectedEndBeforeSelectedStart = isBefore( parseDayToDate(day.dateString), start, // 直接用已经收窄类型的start常量,不需要非空断言 ); if (isSelectedEndBeforeSelectedStart) { setInterval({ start: parseDayToDate(day.dateString), end: start, }); return; } setInterval({ ...interval, end: parseDayToDate(day.dateString) }); return; } // 走到此处时start、end都为非空,无需重复判断 setInterval({ start: parseDayToDate(day.dateString) }); };
这种写法的优势:
- 完全符合
no-non-null-assertion规则要求,没有任何非空断言 - 没有冗余的重复判空逻辑,第一个
!start分支return后,后续代码块里start必然非空,不需要重复判断 - 代码更简洁,直接使用提取后的常量比反复访问
interval.start更短,也避免了重复写属性访问路径的笔误风险
如果你一定要保留布尔类型的判断标记,需要写自定义类型守卫来建立常量和原变量的类型关联,但这种写法冗余度很高,日常开发不推荐使用。
内容的提问来源于stack exchange,提问作者Eik Hunter
相关产品推荐
相关产品推荐

