React Router 如何从路径字符串中推断强类型参数?

How does React Router infer strongly typed arguments from path string?

(这是一个了解工作原理的问题,而不是“我如何...”的问题)

在使用 React Router 时,我意识到 Vscode 当我在这样的路由中输入强类型参数时,IntelliSense 可以向我推荐强类型参数:

<Route path='/users/:userId?' render={props => <UserManager selectedId={props.}/>} />

当我键入 props.match.params. 时,每当我在 path 字符串中输入 :text 作为强类型参数从字符串内部到参数作为强类型 属性(和一个可选参数,如果在我的例子中有问号):

Vscode/TypeScript/React 路由器如何从用户输入的裸字符串生成强类型参数?

请参阅问题仅供参考。

您将在评论中找到解释:

import React from 'react'

type PathParams<
  Path extends string
  > =
  /**
   * Check whether provided argument is valid route string with parameters 
   */
  (Path extends `:${infer Param}/${infer Rest}`
    /**
     * If yes, call PathParams recursively with rest part of the string 
     * and make it union with current param
     */
    ? Param | PathParams<Rest>
    /**
     * Otherwise, check if argument is standalone parameter, for instance ":userId"
     */
    : (Path extends `:${infer Param}`
      /**
       * If yes, return it
       */
      ? Param
      /**
       * Otherwise check if provided string is allowed route string
       * but without /, for instance "user:userId"
       */
      : (Path extends `${infer _Prefix}:${infer Rest}`
        /**
         * If yes, call recursively PathParams with this parameter
         */
        ? PathParams<`:${Rest}`>
        /**
         * If provided string is invalid - return never
         */
        : never)
    )
  )

/**
 * As you might have noticed, we ended up
 * with union of strings. Now we just need to convert this union
 * into record. It is the easiest part
 */
type Params = PathParams<":siteId/user/:userId">

/**
 * Convert union to record with appropriate keys
 */
type PathArgs<Path extends string> = { [K in PathParams<Path>]: string };

// type Result = {
//     siteId: string;
//     userId: string;
// }
type Result = PathArgs<"/dashboard/:siteId/user:userId">

现在让我们尝试构建类似的 React 组件:


type Props<Path extends string> = {
  path: Path,
  params: PathArgs<Path>
}

const Route = <Path extends string>(props: Props<Path>) => {
  return null
}

const jsx = <Route path="/dashboard/:siteId/user:userId" params={{ siteId: 'hello', userId: 'world' }} /> // ok
const jsx_ = <Route path="/dashboard/:siteId/user:userId" params={{ siteId: 'hello' }} /> // error, missing userId prop

Playground

这个例子应该给你一个线索,它是如何在 React 路由器中完成的。我知道我的例子并没有反映 100% 相同的行为,但我仍然相信它是有帮助的

请记住,我的示例比实际实施要小得多。

您可以找到带有注释的 here 原始实现。他们的实现要大得多,因为可能有很多边缘情况。