如何让React组件的泛型参数成为必填项?
如何让React组件的泛型参数成为必填项
问题描述
想要让React组件的泛型参数必须显式指定,要是在JSX里省略了泛型参数(比如写<ChildComponent/>而不是<ChildComponent<Employee/>),TypeScript得直接报类型错误,提示类似“Expected 1 type argument, but got 0.”的信息。
给出的代码示例:
interface Common { id: string; } interface ChildComponentProps<T> { renderFun: (item: T) => React.ReactNode; } const ChildComponent = <T extends Common, >(props: ChildComponentProps<T>) => { return <></>; }; interface Employee extends Common { name: string; position: string; department: string; } const ParentComponent = () => { return ( <> <ChildComponent<Employee> renderFun={(item) => <>{item.department}</>} /> </> ); }; interface Device extends Common { model: string; } const OtherParentComponent = () => { return ( <> <ChildComponent<Device> renderFun={(item) => <>{item.model}</>} /> </> ); };
解决方案
要强制泛型必须显传,核心就是不让TypeScript从组件props里自动推断泛型类型,断了它通过renderFun参数反向推导T的路,这样就只能由开发者手动指定泛型。
方法1:用NoInfer类型打破自动推断
先整个NoInfer工具类型,专门用来阻止TS自动推断泛型:
// 用来阻止TypeScript自动推断泛型的工具类型 type NoInfer<T> = [T][T extends any ? 0 : never]; interface Common { id: string; } interface ChildComponentProps<T> { // 把item的类型改成NoInfer<T>,让TS没法从renderFun里推断出T renderFun: (item: NoInfer<T>) => React.ReactNode; } // 泛型参数不设默认值,强制必须显式传入 const ChildComponent = <T extends Common>(props: ChildComponentProps<T>) => { return <></>; };
这时候要是你省略了泛型参数(比如写<ChildComponent renderFun={(item) => ...} />),TS会直接报错:Expected 1 type argument, but got 0.,完全符合要求。
方法2:给泛型设默认值为never
还有个更简单的办法,给泛型参数设个默认类型never,这样不传泛型的时候,T就变成never,renderFun的参数类型也是never,你在里面用item的属性时就会报错:
interface Common { id: string; } interface ChildComponentProps<T> { renderFun: (item: T) => React.ReactNode; } // 给泛型设默认值为never const ChildComponent = <T extends Common = never>(props: ChildComponentProps<T>) => { return <></>; };
这种方式下,省略泛型会触发类似Property 'department' does not exist on type 'never'.的错误,虽然不是直接提示缺泛型,但同样能逼着你必须显传泛型参数。
说明
方法1的好处是报错提示更精准,直接说缺泛型参数;方法2写起来更省事,适合快速实现。根据自己的需求选就行。
内容的提问来源于stack exchange,提问作者Dimitriy
相关产品推荐
相关产品推荐

