使用// @ts-check时Object.freeze枚举引发类型校验问题求助
解决TS-check下Object.freeze枚举的类型不匹配问题
问题根源
你遇到的报错是因为TypeScript的字面量类型推断:当执行let popupImageAnimationState = PopupImageAnimationState.HIDDEN;时,TS会把变量类型精准锁定为"hidden"(而非所有枚举值的联合类型)。但PopupImageAnimationState.FADE_OUT的类型是"fadeOut",两个字面量类型完全无重叠,TS会判定这种比较无意义,从而抛出错误。
去掉Object.freeze后,TS会把枚举对象的属性类型推断为宽泛的string,变量类型也同步变为string,比较自然不会报错,但枚举对象会失去只读特性,不符合需求。
解决方案
以下是几种在纯JS中保留Object.freeze只读特性,同时解决类型报错的方法:
方案1:用JSDoc定义联合类型并标注变量
通过@typedef定义所有枚举值的联合类型,再给变量标注该类型,让TS明确变量可以是枚举中的任意值:
// @ts-check /** * @typedef { "hidden" | "visible" | "fadeIn" | "fadeOut" | "recenter" | "slideOutToLeft" | "slideOutToRight" | "slideInFromLeft" | "slideInFromRight" } PopupImageAnimationStateType */ /** @type {Readonly<Record<string, PopupImageAnimationStateType>>} */ const PopupImageAnimationState = Object.freeze({ HIDDEN : "hidden", VISIBLE : "visible", FADE_IN : "fadeIn", FADE_OUT : "fadeOut", RECENTER : "recenter", SLIDE_OUT_TO_LEFT : "slideOutToLeft", SLIDE_OUT_TO_RIGHT : "slideOutToRight", SLIDE_IN_FROM_LEFT : "slideInFromLeft", SLIDE_IN_FROM_RIGHT : "slideInFromRight" }); /** @type {PopupImageAnimationStateType} */ let popupImageAnimationState = PopupImageAnimationState.HIDDEN; // 无报错 if(popupImageAnimationState !== PopupImageAnimationState.FADE_OUT) { console.log("not equal"); } switch(popupImageAnimationState) { case PopupImageAnimationState.FADE_IN: case PopupImageAnimationState.RECENTER: case PopupImageAnimationState.SLIDE_IN_FROM_LEFT: case PopupImageAnimationState.SLIDE_IN_FROM_RIGHT: console.log("some of them"); break; default: console.log("the rest"); break; }
方案2:用JSDoc的@enum标签(更简洁)
用@enum标记枚举对象,再通过类型推导自动获取联合类型,无需手动枚举所有字符串值:
// @ts-check /** * @enum {string} */ const PopupImageAnimationState = Object.freeze({ HIDDEN : "hidden", VISIBLE : "visible", FADE_IN : "fadeIn", FADE_OUT : "fadeOut", RECENTER : "recenter", SLIDE_OUT_TO_LEFT : "slideOutToLeft", SLIDE_OUT_TO_RIGHT : "slideOutToRight", SLIDE_IN_FROM_LEFT : "slideInFromLeft", SLIDE_IN_FROM_RIGHT : "slideInFromRight" }); /** @type {typeof PopupImageAnimationState[keyof typeof PopupImageAnimationState]} */ let popupImageAnimationState = PopupImageAnimationState.HIDDEN; // 无报错 if(popupImageAnimationState !== PopupImageAnimationState.FADE_OUT) { console.log("not equal"); } switch(popupImageAnimationState) { case PopupImageAnimationState.FADE_IN: case PopupImageAnimationState.RECENTER: case PopupImageAnimationState.SLIDE_IN_FROM_LEFT: case PopupImageAnimationState.SLIDE_IN_FROM_RIGHT: console.log("some of them"); break; default: console.log("the rest"); break; }
方案3:类型断言快速解决(适合简单场景)
直接用类型断言给变量指定联合类型,省去额外的类型定义:
// @ts-check const PopupImageAnimationState = Object.freeze({ HIDDEN : "hidden", VISIBLE : "visible", FADE_IN : "fadeIn", FADE_OUT : "fadeOut", RECENTER : "recenter", SLIDE_OUT_TO_LEFT : "slideOutToLeft", SLIDE_OUT_TO_RIGHT : "slideOutToRight", SLIDE_IN_FROM_LEFT : "slideInFromLeft", SLIDE_IN_FROM_RIGHT : "slideInFromRight" }); // 用类型断言指定变量为枚举值的联合类型 let popupImageAnimationState = /** @type {typeof PopupImageAnimationState[keyof typeof PopupImageAnimationState]} */ (PopupImageAnimationState.HIDDEN); // 无报错 if(popupImageAnimationState !== PopupImageAnimationState.FADE_OUT) { console.log("not equal"); } switch(popupImageAnimationState) { case PopupImageAnimationState.FADE_IN: case PopupImageAnimationState.RECENTER: case PopupImageAnimationState.SLIDE_IN_FROM_LEFT: case PopupImageAnimationState.SLIDE_IN_FROM_RIGHT: console.log("some of them"); break; default: console.log("the rest"); break; }
内容的提问来源于stack exchange,提问作者Mike McCollister
相关产品推荐
相关产品推荐

