TypeScript 下 React 媒体查询自定义 Hook 类型报错的解决方法
自定义React媒体查询Hook的TypeScript适配方案
问题根因
- 核心类型定义疏漏:生成
mediaQueries时给reduce指定了过于宽泛的泛型Record<string, string | number>,但实际遍历断点配置生成的所有属性值都是字符串格式的媒体查询字符串,根本不存在数字类型的值。这个错误的类型标注直接导致TS将mediaQueries的所有属性值推导为string | number联合类型,传入要求字符串入参的window.matchMedia方法时,就会触发「number类型不能赋值给string类型」的报错。 - 连带的类型定义偏差:Hook内部的回调函数参数类型定义错误,将本该为布尔值的匹配结果标注为string;事件回调参数用any兜底,丢失了类型检查能力;
useScreenQuery的入参没有做类型收窄,任意字符串都可传入,缺少编译期校验。
分步修正方案
- 修正
mediaQueries的核心类型定义
把reduce的泛型从错误的Record<string, string | number>替换为和实际值匹配的Record<string, string>,从根源消除联合类型带来的传参问题:export const mediaQueries = Object.entries( screens ) .reduce<Record<string, string>>( ( acc, [key, val] ) => { const Key = key[0].toUpperCase() + key.substr( 1 ) // 生成CSS格式的媒体查询串 acc[`min` + Key] = `@media ` + min( val ) acc[`max` + Key] = `@media ` + max( val ) // 生成供matchMedia使用的原生JS媒体查询串 acc[`min` + Key + `Js`] = min( val ) acc[`max` + Key + `Js`] = max( val ) return acc }, {} ) - 修正Hook内部的回调与事件类型
替换原来定义错误的回调类型,把无类型的any参数替换为浏览器原生的媒体查询事件类型:// 替换原错误的myCallbackType定义 interface MediaQueryCallback { (isMatch: boolean): void } export const useMediaQuery = ( query: string, cb?: MediaQueryCallback ) => { const [matches, setMatches] = useState( false ) useEffect( () => { const qry = window.matchMedia( query ) setMatches( qry.matches ) // 替换原来的any类型为原生MediaQueryListEvent const handleMatch = ( q: MediaQueryListEvent ) => { setMatches( q.matches ) if ( cb instanceof Function ) cb( q.matches ) } qry.addEventListener( 'change', handleMatch ) return () => qry.removeEventListener( 'change', handleMatch ) }, [query, cb] ) return matches } - 强化
useScreenQuery的编译期类型提示
从原有断点配置推导合法的入参类型,代替原来任意字符串都可传入的宽松定义,实现IDE自动补全和非法入参的编译期校验:// 从screens配置推导合法的断点key类型 type ScreenKey = keyof typeof screens const validKeys = Object.keys( mediaQueries ).filter( key => !key.includes( `Js` ) ) export const useScreenQuery = ( key: ScreenKey, cb?: MediaQueryCallback ) => { const jsQueryKey = `${key}Js` as keyof typeof mediaQueries if ( !mediaQueries[jsQueryKey] ) throw new TypeError( `useScreenQuery received an invalid key: ${key}.` + ` Should be one of ${validKeys.join(', ')}` ) return useMediaQuery( mediaQueries[jsQueryKey], cb ) }
修正后效果
所有类型不匹配的报错会完全消除,调用Hook时可以获得完整的类型提示:输入断点key时会自动提示所有合法选项,媒体查询串会被严格校验为字符串类型,回调函数的入参会明确提示为布尔值的匹配状态,不再需要靠运行时校验排查入参错误。
内容的提问来源于stack exchange,提问作者Tyler Norlund
相关产品推荐
相关产品推荐

