如何在 Typescript 中正确使用 React.lazy() 来导入带有泛型类型参数的反应组件?
Posted
技术标签:
【中文标题】如何在 Typescript 中正确使用 React.lazy() 来导入带有泛型类型参数的反应组件?【英文标题】:How do you correctly use React.lazy() in Typescript to import a react component with a generic type parameter? 【发布时间】:2020-08-23 07:53:29 【问题描述】:(在https://github.com/codingismy11to7/lazy-component-typing 重现)
我猜这是@types/react
和LazyExoticComponent
定义的问题...
在带有类型参数的组件上使用React.lazy()
时,Typescript 无法再编译
项目。
给定组件:
import React from "react";
export interface Props<T>
value: T;
valueCallback: (t: T) => void;
export default function TypedComponent<T>(props: Props<T>)
return (
<>
<div>`$props.value`</div>
<button onClick=() => props.valueCallback(props.value)>click</button>
</>
)
这样编译:
import React, useState from "react";
import TypedComponent from "./TypedComponent";
export default function StrictComponent()
const [state, setState] = useState("blah");
return (
<TypedComponent value=state valueCallback=setState/>
);
虽然没有:
import React, lazy, useState from "react";
const TypedComponent = lazy(() => import("./TypedComponent"));
export default function LazyComponent()
const [state, setState] = useState("blah");
return (
<TypedComponent value=state valueCallback=setState/>
);
TypeScript error in D:/dev/lazy-component-typing/src/LazyComponent.tsx(9,35):
Type 'Dispatch<SetStateAction<string>>' is not assignable to type '(t: unknown) => void'.
Types of parameters 'value' and 't' are incompatible.
Type 'unknown' is not assignable to type 'SetStateAction<string>'.
Type 'unknown' is not assignable to type '(prevState: string) => string'. TS2322
7 |
8 | return (
> 9 | <TypedComponent value=state valueCallback=setState/>
| ^
10 | );
11 |
12 |
【问题讨论】:
你找到合适的解决方案了吗? 【参考方案1】:TL;DR:
您可以覆盖 React.lazy
类型定义以支持泛型。
react.d.ts:
declare namespace React
function lazy<T extends ComponentType<any>>(
factory: () => Promise< default: T >,
): T;
说明:原始React.lazy
声明如下所示:
function lazy<T extends ComponentType<any>>(
factory: () => Promise< default: T >
): LazyExoticComponent<T>;
它返回LazyExoticComponent
类型,它是ExoticComponent
的超集。不幸的是,这个接口的编写方式不支持泛型。但是,根据comment above the interface,它仅用于内部区分“常规”和其他组件,例如lazy
和memo
调用的结果;在 JSX 语法中,ExoticComponent
和 Component
之间没有区别。这就是为什么我们可以安全地在自定义覆盖中省略 LaxyExoticComponent
类型并使用泛型。
【讨论】:
【参考方案2】:不是理想的解决方案,但我能够通过导出组件的类型来实现这一点,然后使用它来转换 React.lazy 的结果
export type TypedComponentType = typeof TypedComponent;
然后
import type TypeComponent from './TypedComponent';
const TypedComponent = lazy(() => import("./TypedComponent")) as TypedComponentType;
【讨论】:
我喜欢这个想法,但我只是将其转换为typeof TypedComponent
,而不是为此目的创建新类型。【参考方案3】:
TSX 似乎不能很好地处理 props 中的类型参数。
你应该设置一个默认的类型参数值,例如any
,但这只是假设你的类型在使用组件时总是any
,这可能不是你想要的:
export interface Props<T = any>
value: T;
valueCallback: (t: T) => void;
// ...
// `valueCallback` will pass an explicit `any`
<TypedComponent value=value valueCallback=setState />
您可以尝试使用有效的扩展类型复制它,然后使用它,例如:
import Props as TCProps from './TypedComponent'
const StronglyTypedComponent: React.Component<TCProps<string>> = TypedComponent
// ...
// correctly typed, but more verbal
<StronglyTypedComponent value=value valueCallback=setState />
【讨论】:
TSX 在像你写的那样处理我的类型参数的情况下,问题只有在与React.lazy()
结合时才会发生。以上是关于如何在 Typescript 中正确使用 React.lazy() 来导入带有泛型类型参数的反应组件?的主要内容,如果未能解决你的问题,请参考以下文章
使用 create-react-app 和 Typescript 时如何在构建时加载 .md 文件?
如何在 Typescript 中正确使用环境变量? [复制]
Typescript - 如何在 Angular2 中正确使用 jsPDF?
如何使用 Typescript 在 mongoose 中正确键入对象 ID 数组