首页 > 解决方案 > 反应路线渲染属性类型挑战打字稿

问题描述

我有一些组件道具project通过 a 传递LinkRoute这样的(我的项目对象进入state扩展属性):

<Link
  to={{
    pathname: path,
    state: {
      project,
    },
  }}
  key={project.id}
>
  <ProjectSummary project={project} deleteCallback={projectDelete}/>
</Link>

然后它在路由中接收并可以像这样传递给链接的组件:

<Route
  path='/project/:id'
  render={({ location }:any) => { //TYPE CHALLENGE HERE
    const { state } = location;
    return <ProjectDetails project={state.project} /> 
  }}
/>

找到any带有//TYPE CHALLENGE HERE注释的类型。我尝试了很多类型的 'react-router' 和 'react-router-dom' 并且无法找到匹配的类型。

最接近的似乎是这个:

export interface RouteComponentProps<
    Params extends { [K in keyof Params]?: string } = {},
    C extends StaticContext = StaticContext,
    S = H.LocationState
> {
    history: H.History<S>;
    location: H.Location<S>;
    match: match<Params>;
    staticContext?: C;
}

由于组件从该接口接收所有路由参数,但我扩展了location传入我的项目对象的位置。如果它有任何相关性,这是project我传入的对象的类型:

export interface IFirebaseProject {
  id: string,
  authorFirstName: string,
  authorId: string,
  authorLastName: string
  content: string
  createdAt: firebase.firestore.Timestamp //firebase timestamp
  title: string
}

我还发布了我认为最接近的方法的错误:

render={({ location }:RouteComponentProps<any, StaticContext, IFirebaseProject>) => {}
No overload matches this call.
  Overload 1 of 2, '(props: RouteProps | Readonly<RouteProps>): Route<RouteProps>', gave the following error.
    Type '({ location }: RouteComponentProps<any, StaticContext, IFirebaseProject>) => JSX.Element' is not assignable to type '(props: RouteComponentProps<any, StaticContext, unknown>) => ReactNode'.
      Types of parameters '__0' and 'props' are incompatible.
        Type 'RouteComponentProps<any, StaticContext, unknown>' is not assignable to type 'RouteComponentProps<any, StaticContext, IFirebaseProject>'.
          Type 'unknown' is not assignable to type 'IFirebaseProject'.
  Overload 2 of 2, '(props: RouteProps, context: any): Route<RouteProps>', gave the following error.
    Type '({ location }: RouteComponentProps<any, StaticContext, IFirebaseProject>) => JSX.Element' is not assignable to type '(props: RouteComponentProps<any, StaticContext, unknown>) => ReactNode'.ts(2769)
index.d.ts(89, 5): The expected type comes from property 'render' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Route<RouteProps>> & Readonly<RouteProps> & Readonly<...>'
index.d.ts(89, 5): The expected type comes from property 'render' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Route<RouteProps>> & Readonly<RouteProps> & Readonly<...>'

编辑:

更新了日志错误的屏幕截图render在此处输入图像描述

render={({ location }: { location: Location<{ project: IFirebaseProject }> }) => {尝试的错误的文本全文:

No overload matches this call.
  Overload 1 of 2, '(props: RouteProps | Readonly<RouteProps>): Route<RouteProps>', gave the following error.
    Type '({ location }: { location: Location<{    project: IFirebaseProject;}>; }) => JSX.Element' is not assignable to type '(props: RouteComponentProps<any, StaticContext, unknown>) => ReactNode'.
      Types of parameters '__0' and 'props' are incompatible.
        Type 'RouteComponentProps<any, StaticContext, unknown>' is not assignable to type '{ location: Location<{ project: IFirebaseProject; }>; }'.
          Types of property 'location' are incompatible.
            Type 'Location<unknown>' is not assignable to type 'Location<{ project: IFirebaseProject; }>'.
              Type 'unknown' is not assignable to type '{ project: IFirebaseProject; }'.
  Overload 2 of 2, '(props: RouteProps, context: any): Route<RouteProps>', gave the following error.
    Type '({ location }: { location: Location<{    project: IFirebaseProject;}>; }) => JSX.Element' is not assignable to type '(props: RouteComponentProps<any, StaticContext, unknown>) => ReactNode'.ts(2769)
index.d.ts(89, 5): The expected type comes from property 'render' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Route<RouteProps>> & Readonly<RouteProps> & Readonly<...>'
index.d.ts(89, 5): The expected type comes from property 'render' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Route<RouteProps>> & Readonly<RouteProps> & Readonly<...>'

标签: typescriptreact-routerreact-router-dom

解决方案


RouteComponentProps

你是如此接近!S参数RouteComponentProps控制 the和props的state类型,所以这就是你想要使用的。locationhistory

错误是您正在设置SIFirebaseProject意味着状态本身就是一个IFirebaseProject. 但现实中的状态是这样的{project}。它是一个project具有值为 的键的对象IFirebaseProject。所以正确的类型S{project: IFirebaseProject}.

正确设置所有类型后,它是这样的:

render={({ location }: RouteComponentProps<{id: string}, StaticContext, {project: IFirebaseProject}>)  => {

location不使用其他两个泛型,所以这很好:

render={({ location }: RouteComponentProps<any, any, {project: IFirebaseProject}>)  => {

地点

就我个人而言,我只会为该属性声明一个类型,因为这是您实际需要location的唯一属性。RouteComponentProps我不喜欢使用依赖于未使用的泛型的类型。

底层Location类型来自历史包,它是 react-router 的依赖项。它的类型被导入到 react-router-dom 类型中import * as H from "history",所以这就是你看到H.Location. 但是你可以直接导入它:

import {Location} from "history";

并像这样使用它:

render={({ location }: {location: Location<{project: IFirebaseProject}>})  => {

打字稿游乐场链接

编辑:render对。component

您的 CodeSandbox 中发生了一些奇怪的事情,因为我确实看到了错误,但我无法在 TS Playground 中单独重现它。与其把我们的头撞到墙上,不如使用一种稍微不同的方法来规避错误。

props的component类型比render它允许的 props 类型要宽松得多,因为它允许 props 类型是RouteComponentProps<any>(我们期望的)或any(字面上的任何东西)。只需将道具名称从 切换rendercomponent,它就会突然起作用!我们不想内联定义组件,因为它将在每次渲染时重新创建,但我们可以在外部定义它。

我们现在将使用componentpropRoute代替render函数:

<Route path="/project/:id" component={ProjectScreen} />

有两个选项可以访问ProjectScreen似乎都对我有用的位置。

  1. 我们可以通过组件中注入的 props 来访问它。这正是我们之前所拥有的,但它是允许的,component而不是render由于any道具上的类型。
const ProjectScreen = ({ location }: { location: Location<{ project: IFirebaseProject }>; }) => {
  const { state } = location;
  return <ProjectDetails project={state.project} />;
};
  1. useLocation我们可以使用钩子通过路由器上下文访问它。这个钩子根据状态是通用的,所以我们可以在调用它的时候指定状态类型。我们定义了一个组件,它不接受任何道具,但会ProjectDetails根据钩子的位置呈现适当的。
const ProjectScreen = () => {
  const { state } = useLocation<{ project: IFirebaseProject }>();
  return <ProjectDetails project={state.project} />;
}

推荐阅读