TypeScript中的嵌套递归类型提示?

Posted

技术标签:

【中文标题】TypeScript中的嵌套递归类型提示?【英文标题】:Nested recursive Type Hinting in TypeScript? 【发布时间】:2021-12-05 11:50:59 【问题描述】:

我不完全确定我是否正确,所以我会解释一下。

我正在使用 TypeScript 创建 React 应用程序,为了让我的生活更简单,我想将所有路由路径分别存储为常量,我还希望在 VS Code 中拥有像自动完成这样的 TypeScript。所以我有这个结构之王:

type GlobalRoutingT = 'user' | 'listing';

type RoutingVariants = 
  list: string,
  add: string,
  get: string,
  [key: string]: RoutingVariants | string,
;

type RouterT = Record<GlobalRoutingT, RoutingVariants>;

export const Routes: RouterT = 
  user: 
    list: '/user',
    add: '/user/add',
    get: '/user/:id',
    invoices: 
      list: '/user/invoices',
      add: '/user/invoices/add',
      get: '/user/invoices/:id',
    
  ,
  listing: 
    list: '/listing',
    add: '/listing/add',
    get: '/listing/:id',
  ,
;

效果很好,我现在可以这样使用它

<Route path=Routes.user.invoices.get exact >
  <GetInvoice />
</Route>

但是在 VS Code 里面我得到了错误:

类型'string | 上不存在属性'get'路由变体'。类型“字符串”上不存在属性“获取”。

和 Routes.user.invoices.get 部分突出显示。

我做错了什么?

【问题讨论】:

问题是[key: string]: RoutingVariants | stringRoutingVariantsany 属性可以有string 作为类型,所以Routes.user.invoices 在技术上可以是一个字符串。我还不确定如何解决这个问题。 @Cerbrus 听起来不错,但令人沮丧,我不明白这是 eslint 问题还是我只是不擅长 TypeScript。 你看过我的回答了吗?我认为这与您所描述的最接近。 【参考方案1】:

您想要的似乎不太可能: How to combine known interface properties with a custom index signature?

您不能将索引签名与同一接口/类型上的命名键结合起来。

作为一种解决方法,您可以使用“子”属性,如下所示:

type GlobalRoutingT = 'user' | 'listing';

type RoutingVariants = 
    list: string,
    add: string,
    get: string,
    child?: Record<string, RoutingVariants>
;

type RouterT = Record<GlobalRoutingT, RoutingVariants>;

export const Routes: RouterT = 
    user: 
        list: '/user',
        add: '/user/add',
        get: '/user/:id',
        child: 
            invoices: 
                list: '/user/invoices',
                add: '/user/invoices/add',
                get: '/user/invoices/:id'
            
        
    ,
    listing: 
        list: '/listing',
        add: '/listing/add',
        get: '/listing/:id'
    
;

const a = Routes.user.child?.invoices.get;

请注意我必须如何使用 ?. 来访问孩子,因为它在类型定义中是可选的。

您可以将 child 替换为更短的字母、单个字母、$_...

【讨论】:

抱歉耽搁了,这看起来是最严格的做法。谢谢【参考方案2】:

你需要强制类型转换,试试这个:

<Route path=(Routes.user.invoices as RoutingVariants).get exact >
  <GetInvoice />
</Route>

【讨论】:

有效,但如果继续嵌套,这将很快堆叠;'(

以上是关于TypeScript中的嵌套递归类型提示?的主要内容,如果未能解决你的问题,请参考以下文章

在 Typescript 2.0 中使用 @types 类型定义

TypeScript:推断嵌套联合类型的类型

Typescript 无法检查函数返回类型的嵌套对象

递归排除 Typescript 中的只读属性

TypeScript中的实用工具类型(Utility Types)

访问从 GraphQL 生成的多个嵌套 TypeScript 类型