TypeScript可选链(?.)在不等式中使用报错的原因及简写方案
为什么getSize1()会报错?
当你使用options?.maxHeight时,这个表达式的类型是number | undefined。TypeScript会严格检查类型兼容性:height是确定的number类型,而number > (number | undefined)这种比较在逻辑上是有问题的——如果options.maxHeight是undefined,height > undefined在JavaScript中会返回NaN,这并不是一个有效的布尔值。所以TypeScript会提示你“变量可能未定义”,本质是在阻止这种无意义的类型比较。
getSize1()和getSize2()中if语句的核心区别
getSize1()的条件:height > options?.maxHeight
可选链?.只会安全访问属性,但不会帮你做类型收窄。即使options存在,options.maxHeight仍可能是undefined,导致比较逻辑不合法,TypeScript直接报错阻止。getSize2()的条件:options.maxHeight && height > options.maxHeight
这里利用了JavaScript的短路求值:第一个options.maxHeight会先判断值是否为“真值”(排除了undefined、0、NaN等)。当这个条件为真时,TypeScript会自动将options.maxHeight的类型收窄为number(因为只有number类型的真值才会通过第一个判断),所以第二个比较height > options.maxHeight就变成了合法的number > number,不会报错。
⚠️ 注意:这种写法有个潜在问题——如果maxHeight被设置为0,options.maxHeight会被当成false,导致条件不执行,而0其实是一个有效的最大值,这可能不符合你的预期。
getSize2()的更简洁(且更安全)写法
如果你想保留对0的支持,同时简化代码,可以用明确的类型守卫来替代真值判断:
写法1:判断是否为undefined
function getSize3(width: number, options: Options) { const height = width * 2; if (options.maxHeight !== undefined && height > options.maxHeight) { const newWidth = options.maxHeight / 2; return [newWidth, height]; } return [width, height]; }
这种写法明确排除了undefined的情况,同时允许maxHeight为0,逻辑更严谨。
写法2:用typeof做类型守卫
function getSize4(width: number, options: Options) { const height = width * 2; if (typeof options.maxHeight === 'number' && height > options.maxHeight) { const newWidth = options.maxHeight / 2; return [newWidth, height]; } return [width, height]; }
typeof options.maxHeight === 'number'会精确地将类型收窄为number,完全避免了真值判断的陷阱。
写法3:利用可选链结合空值合并(适合默认值场景)
如果希望maxHeight未定义时使用一个默认值(比如无穷大,让条件永远不触发),可以这样写:
function getSize5(width: number, options: Options) { const height = width * 2; const maxHeight = options?.maxHeight ?? Infinity; if (height > maxHeight) { const newWidth = maxHeight / 2; return [newWidth, height]; } return [width, height]; }
这里??是空值合并运算符,只有当options?.maxHeight为undefined或null时,才会使用Infinity作为默认值,逻辑清晰且简洁。
内容的提问来源于stack exchange,提问作者Antyos

