V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
jerrry
V2EX  ›  程序员

TS as const 类型提示问题

  •  
  •   jerrry · 2023-05-30 11:10:55 +08:00 · 1317 次点击
    这是一个创建于 573 天前的主题,其中的信息可能已经有所发展或是发生改变。
    const OrderStatus = {
      AWAITING_PAYMENT: {
        value: 'AWAITING_PAYMENT',
        label: '待支付'
      },
      AWAITING_DELIVER: {
        value: 'AWAITING_DELIVER',
        label: '待发货'
      },
      ...
    } as const
    

    现在 OrderStatus.AWAITING_PAYMENT 是有类型提示 { value: 'AWAITING_PAYMENT', label: '待支付' } 的,我想写一个方法转换一下这个结构,变成

    const OrderStatusEnum = {
      AWAITING_PAYMENT: 'AWAITING_PAYMENT',
      AWAITING_DELIVER: 'AWAITING_DELIVER'
    } as const
    

    转换方式是这样写的:

    const OrderStatusEnum = Object.keys(OrderStatus).reduce((acc, key) => {
    	acc[key] = OrderStatus[key].value
        return acc
    }, {})
    

    但是这样的方式 OrderStatusEnum 是没有类型提示的,怎么定义类型才能让 OrderStatusEnum.AWAITING_PAYMENT 会提示 'AWAITING_PAYMENT'?

    10 条回复    2023-05-30 13:18:55 +08:00
    lisongeee
        1
    lisongeee  
       2023-05-30 11:24:15 +08:00   ❤️ 1
    ```ts
    const OrderStatusEnum = Object.keys(OrderStatus).reduce((acc, key) => {
    // @ts-ignore
    acc[key] = OrderStatus[key].value;
    return acc;
    }, {}) as {
    [P in keyof typeof OrderStatus]: (typeof OrderStatus)[P]['value'];
    };
    ```
    paledream
        3
    paledream  
       2023-05-30 11:34:19 +08:00   ❤️ 1
    ```ts

    const OrderStatus = {
    AWAITING_PAYMENT: {
    value: 'AWAITING_PAYMENT',
    label: '待支付'
    },
    AWAITING_DELIVER: {
    value: 'AWAITING_DELIVER',
    label: '待发货'
    }
    } as const

    type OrderStatusType = typeof OrderStatus

    type OrderStatusKey = keyof OrderStatusType

    type Enum = {
    [key in OrderStatusKey]: OrderStatusType[key]['value']
    }

    const OrderStatusEnum = Object.keys(OrderStatus).reduce((acc, key) => {
    acc[key] = OrderStatus[key].value
    return acc
    }, {} as Enum)

    ```


    写完后发现和 1L 差不多
    jerrry
        4
    jerrry  
    OP
       2023-05-30 11:35:05 +08:00
    @lisongeee @ChanKc 非常感谢,问题解决了,看来 TS 的类型还要多学习才是!
    IvanLi127
        5
    IvanLi127  
       2023-05-30 12:26:22 +08:00   ❤️ 1
    我也来个,和楼上思路也是一样的,但是我觉得优雅点 哈哈:

    ```ts
    type enumKeys = typeof OrderStatus[keyof typeof OrderStatus]['value'];

    const keys = Object.keys(OrderStatus) as (keyof typeof OrderStatus)[];
    const OrderStatusEnum = keys.reduce((acc, key) => {
    acc[key] = OrderStatus[key].value;
    return acc;
    }, {} as Record<enumKeys, string>);
    ```
    gogogo1203
        6
    gogogo1203  
       2023-05-30 12:33:20 +08:00
    我是真的讨要 keyof typeof OrderStatus. 难度得要死. 类似这种,我都让 gpt4 写,能不报错就行
    gogogo1203
        7
    gogogo1203  
       2023-05-30 12:39:09 +08:00
    const OrderStatusEnum = ['AWAITING_PAYMENT', 'AWAITING_DELIVER'] as const
    (typeof OrderStatusEnum)[number] gpt4 推荐的方法. ts 一般用一些模板,需要用的时候再改改就好。 太复杂的 type inference 太难度了, 根本就是本末倒置. 不如直接 @ts-ignore
    jerrry
        8
    jerrry  
    OP
       2023-05-30 13:05:18 +08:00
    @lisongeee @paledream
    麻烦再问下,如果封装成一个函数的形式,类型该怎么定义?

    ```typescript
    function generateEnum<K extends string, V extends string>(keyMap: { [key in K]: { label: V; value: V }}) {
    return Object.keys(keyMap).reduce((acc, key) => {
    acc[key] = keyMap[key].value
    return acc
    }, {} as { [key in keyof typeof keyMap]: (typeof keyMap)[key]['value'] })
    }

    const OrderStatusEnum = generateEnum(OrderStatus)
    ```

    这个时候如果调用 OrderStatusEnum.AWAITING_PAYMENT 类型提示并不是 'AWAITING_PAYMENT',是不是哪里写错了?
    otakustay
        9
    otakustay  
       2023-05-30 13:16:44 +08:00
    主要问题就是,Object.keys 它没泛型,然后你 reduce 运算后的类型和初始类型({})也是不一样的,TS 追踪不到 reduce 内部的 assignment 对类型的改变,所以你只能自己强转了
    jerrry
        10
    jerrry  
    OP
       2023-05-30 13:18:55 +08:00
    @lisongeee @paledream
    不好意思,是我写错了,这样就对咯:
    ```typescript
    function generateEnum<T extends Record<string, { value: string; label: string }>>(keyMap: T) {
    return (Object.keys(keyMap) as (keyof T)[]).reduce((acc, key) => {
    acc[key] = keyMap[key].value
    return acc
    }, {} as { [key in keyof T]: T[key]['value'] })
    }
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3133 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 13:01 · PVG 21:01 · LAX 05:01 · JFK 08:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.