如何为React.memo包装的组件添加静态属性并实现TypeScript类型推断
问题描述
核心问题
给普通函数添加额外属性时,TypeScript能自动推断属性类型;但给React.memo返回的组件添加额外属性时,TypeScript无法自动推断类型。
示例代码:
// 期望像给foo添加属性一样,给Foo = React.memo(foo)实现相同的类型推断 const Foo = React.memo(foo) Foo.cssset = { root: css``, bar: css``, } Foo.cssset // 期望类型为 { root: number, bar: number } //^? Foo.cssset.root // 期望类型为 number //^?
2023/08/10 更新需求
希望给React.memo包装后的组件配置静态属性(如内部使用的CSS类名),实现:
- 组件内部能直接通过组件名访问静态属性
- 外部(如设计师)只需记住导出的组件名,就能直接获取静态属性,无需额外导入原始函数
期望实现的代码结构:
export const Foo = React.memo(() => { return ( <div css={[Foo.cssset.root]}> <div css={[Foo.cssset.areas.filter]}> <SimulatedButton /> <SymbolSelector /> </div> <div css={[Foo.cssset.areas.status]}> <OverviewsStatus /> </div> <div css={[Foo.cssset.areas.list]}> <DataList /> </div> </div> ) }) Foo.cssset = { root: 'root-fj289uf', areas: { filter: 'filter-fjiu128', status: 'filter-fkj2if8', list: 'filter-vjk38f', }, }
设计师使用场景:
<div id='layout' css={css` .${Foo.cssset.root} { // 直接通过导出的组件名访问静态属性 } `} ></div>
解决方案
方法一:手动扩展组件类型
先定义静态属性的类型,再通过类型断言给React.memo返回的组件扩展类型:
import React from 'react' // 定义cssset的类型 type CssSet = { root: string areas: { filter: string status: string list: string } } // 定义带静态属性的组件类型 type MemoComponentWithCssSet = React.FC & { cssset: CssSet } // 创建组件并赋值静态属性 export const Foo = React.memo(() => { return ( <div css={[Foo.cssset.root]}> <div css={[Foo.cssset.areas.filter]}> <SimulatedButton /> <SymbolSelector /> </div> <div css={[Foo.cssset.areas.status]}> <OverviewsStatus /> </div> <div css={[Foo.cssset.areas.list]}> <DataList /> </div> </div> ) }) as MemoComponentWithCssSet // 赋值静态属性,TypeScript会自动校验类型 Foo.cssset = { root: 'root-fj289uf', areas: { filter: 'filter-fjiu128', status: 'filter-fkj2if8', list: 'filter-vjk38f', }, }
方法二:使用工具函数自动推断类型
如果不想手动定义CssSet类型,可以写一个工具函数自动推断静态属性类型,同时保留组件原有类型:
import React, { memo, FC } from 'react' // 工具函数:给memo组件添加静态属性并自动推断类型 function memoWithStaticProps<T extends FC, S>(Component: T, staticProps: S): T & S { const Memoized = memo(Component) Object.assign(Memoized, staticProps) return Memoized as T & S } // 使用工具函数创建组件 export const Foo = memoWithStaticProps(() => { return ( <div css={[Foo.cssset.root]}> <div css={[Foo.cssset.areas.filter]}> <SimulatedButton /> <SymbolSelector /> </div> <div css={[Foo.cssset.areas.status]}> <OverviewsStatus /> </div> <div css={[Foo.cssset.areas.list]}> <DataList /> </div> </div> ) }, { cssset: { root: 'root-fj289uf', areas: { filter: 'filter-fjiu128', status: 'filter-fkj2if8', list: 'filter-vjk38f', }, } })
方法三:简化类型断言实现
如果仅给单个组件添加属性,可直接用类型断言扩展,无需额外工具函数:
import React from 'react' export const Foo = React.memo(() => { return ( <div css={[(Foo as React.FC & { cssset: CssSet }).cssset.root]}> <div css={[(Foo as React.FC & { cssset: CssSet }).cssset.areas.filter]}> <SimulatedButton /> <SymbolSelector /> </div> {/* ...其他内容 */} </div> ) }) // 给组件添加静态属性并指定类型 type CssSet = { root: string areas: { filter: string status: string list: string } } (Foo as React.FC & { cssset: CssSet }).cssset = { root: 'root-fj289uf', areas: { filter: 'filter-fjiu128', status: 'filter-fkj2if8', list: 'filter-vjk38f', }, }
关键原理
React.memo返回的类型是React.MemoExoticComponent<T>,默认不包含自定义静态属性。通过**类型交集(&)**将组件类型与静态属性类型合并,即可让TypeScript识别自定义属性。工具函数方式通过泛型自动推断静态属性类型,减少手动定义的工作量。
内容的提问来源于stack exchange,提问作者Triple Top Patterns
相关产品推荐
相关产品推荐

