typescript 在 Object.entries 的回调中丢失了类型怎么办? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
xiaoming1992
V2EX    Javascript

typescript 在 Object.entries 的回调中丢失了类型怎么办?

  •  
  •   xiaoming1992 2019-08-27 16:33:26 +08:00 6631 次点击
    这是一个创建于 2239 天前的主题,其中的信息可能已经有所发展或是发生改变。
    type Vegetable = "茄子" | "黄瓜" | "西红柿" type List = { [key in Vegetable]: { price: number; num: number; }; } const list: List = { "茄子": { price: 5, num: 2 }, "西红柿": { price: 5, num: 2 }, "黄瓜": { price: 5, num: 2 }, } Object.entries(list).map(([key, item]) => { // 这儿的 key 不再是 Vegetable, 而是 string, 怎么能维持它的 Vegetable 类型呢? }) 
    31 条回复    2019-08-29 00:56:41 +08:00
    geekdada
        1
    geekdada  
       2019-08-27 16:46:51 +08:00
    运行在 Runtime 里的还是 JS 代码,所以你在运行时拿到的类型是 string。
    ochatokori
        2
    ochatokori  
       2019-08-27 16:49:26 +08:00 via Android
    可能我不会 ts,用 ts 有很多这样类似的问题
    实在不行就<Vegetable>
    Hypn0s
        3
    Hypn0s  
       2019-08-27 16:51:53 +08:00
    ```Javascript
    Object.entries(list).map(([trueKey, item]) => {
    let key1 = <Vegetable>trueKey;
    let key2 = trueKey as Vegetable;
    })
    ```
    实在没办法
    azh7138m
        4
    azh7138m  
       2019-08-27 16:52:16 +08:00   1
    entries 定义是
    entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];
    entries(o: {}): [string, any][];
    你得自己提供一个 entries 来 hack 这个 key 的类型
    azh7138m
        5
    azh7138m  
       2019-08-27 17:04:45 +08:00
    目前在 ts 里面构造出一个数组长度是一个对象 key 的数量,这种操作很难做通用实现
    value 类型固定的,给个 xjb 的做法的话
    在代码前面写一个
    interface ObjectConstructor {
    entries<O>(o: O): [keyof O, O[keyof O]][];
    }
    后面的 entries 就能拿到类型了
    但是我不推荐这样子写,因为不通用
    如果场景足够单一,可以补充一个 entries 的定义,如果还有别的场景,建议手动指定 key 的类型
    wawaforya
        6
    wawaforya  
       2019-08-27 17:05:29 +08:00
    ```
    Object.entries(list).map(([key, item]: [Vegetable, { price: number; num: number }]) => {
    // 可以给参数加个类型定义
    });
    ```
    xiaoming1992
        7
    xiaoming1992  
    OP
       2019-08-27 17:29:35 +08:00
    @wawaforya 没用,提示不能将 string 赋给 Vegetable
    jatai
        8
    jatai  
       2019-08-27 17:33:05 +08:00 via Android
    什么? ts 也设置类型? 前端高手不都是用 any 吗
    xiaoming1992
        9
    xiaoming1992  
    OP
       2019-08-27 17:33:45 +08:00
    @wawaforya 没用,提示不能将 string 分配给 Vegetable 类型;

    @Hypn0s 我确实是这样做的,但是如果很多地方要用到这个东西,每一处都要写很烦;

    @azh7138m 写这样的 interface 给我的感觉好像是 js 里面重写原型方法一样,不太喜欢这样写,虽然这样确实解决问题;
    xiaoming1992
        10
    xiaoming1992  
    OP
       2019-08-27 17:34:19 +08:00
    可能因为我菜吧 /(ㄒoㄒ)/~~
    azh7138m
        11
    azh7138m  
       2019-08-27 17:43:14 +08:00
    @xiaoming1992 并不,他们不是一个概念,补充一个新的签名是重载,js 里面没有这个概念
    xiaoming1992
        12
    xiaoming1992  
    OP
       2019-08-27 17:56:22 +08:00
    @azh7138m 不是说`重载`必须要将`函数定义`紧挨着`interface`写吗?还可以半中间插进来?王自如觉得很 awesome
    optional
        14
    optional  
       2019-08-27 18:19:26 +08:00
    type List = {
    [key in Vegetable]: {
    price: number;
    num: number;
    };
    }
    这只能保证 list[Vegetable] 的 type 是 {price,num},并不能保证所有 key 都是{price,num}
    不信你试试
    const list = {} as List
    const o = list['any']
    list['xx'] = 1;
    是合法的,list['any'] 的类型是 any

    能保证的是
    type List = {
    [key : Vegetable]: {
    price: number;
    num: number;
    };
    }
    但是这又是不合法的。
    wawaforya
        15
    wawaforya  
       2019-08-27 18:27:08 +08:00   2
    @xiaoming1992 , 我这边可以的啊,难道是版本问题?我 typescript 的版本是 3.5.3

    PS: List 其实可以用 Record 来定义,Record<Vegetable, { price: number; num: number }>
    xiaoming1992
        16
    xiaoming1992  
    OP
       2019-08-27 18:41:28 +08:00 via Android
    @optional 声明里面没有"xx"这个 key,你添加`list['xx'] = 1;`不会报错吗?难道是因为我开了严格模式的缘故?等我回去看看。


    @azh7138m 感谢,等我吃完饭回去看看。
    optional
        17
    optional  
       2019-08-27 18:45:41 +08:00
    @xiaoming1992 不会报错
    zbinlin
        18
    zbinlin  
       2019-08-27 22:00:17 +08:00
    entries 的签名写死了 string,你试试加上这个自动推导的签名:

    declare global {
    interface ObjectConstructor {
    entries<S extends string, T>(list: { [key in S]: T }): [S, T][];
    }
    }
    xiaoming1992
        19
    xiaoming1992  
    OP
       2019-08-27 23:08:09 +08:00 via Android
    @zbinlin .如果这么搞的话,那什么 map foreach 什么的都要声明。。。
    zbinlin
        20
    zbinlin  
       2019-08-28 00:12:07 +08:00
    @xiaoming1992 不需要呀,为什么需要,map, foreach 这些已经能正确地推导出数组里的类型的。
    xiaoming1992
        21
    xiaoming1992  
    OP
       2019-08-28 06:57:47 +08:00 via Android
    @zbinlin 对,被搞懵了
    Oucreate
        22
    Oucreate  
       2019-08-28 13:47:12 +08:00   1
    @azh7138m #4
    > entries 定义是
    > entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][]

    虽然的确如此(可见于文件`lib.es2017.object.d.ts`),但我想不出需要 `<T>` 的情形,你能不能举个例子?谢谢啦!
    azh7138m
        23
    azh7138m  
       2019-08-28 13:57:39 +08:00
    @Oucreate 你就当它是 Map<string, T>吧
    xiaoming1992
        24
    xiaoming1992  
    OP
       2019-08-28 15:03:08 +08:00
    @wawaforya 才知道 Record 这么个东西,对我的类型定义是个很好的补充,感谢。虽然还是没什么卵用。。。


    @zbinlin 写到 global 上不知道会不会对编译产生什么负担?

    不知道怎么,按照 @azh7138m 写的,添加一个定义
    ``` typescript
    interface ObjectConstructor {
    entries<S extends string, T>(o: Record<S, T>): [S, T][];
    }
    ```
    没起作用。

    或许 @optional 是对的,我不应该默认生成的 key 是 Vegetable,应该再次校验一下。
    Oucreate
        25
    Oucreate  
       2019-08-28 15:08:52 +08:00   1
    @azh7138m 呃,我还是不太明白……

    为什么不都是第二种定义的 `any`,毕竟值不都是任意类型的么,所以会有什么特殊的情形需要 `<T>`?
    Oucreate
        26
    Oucreate  
       2019-08-28 15:12:15 +08:00
    修正:
    “毕竟值不都是任意类型的么”

    “毕竟值不都 *可以* 是任意类型的么”
    azh7138m
        27
    azh7138m  
       2019-08-28 15:45:31 +08:00
    @xiaoming1992
    - 我没有写 extends


    @Oucreate
    特殊的情形 -> 把对象当 Map<string, T>用的时候
    lhc70000
        28
    lhc70000  
       2019-08-28 16:17:15 +08:00
    ```
    ```ts
    type Vegetable = "茄子" | "黄瓜" | "西红柿";
    type Value = { price: number, num: number };

    const list: Record<Vegetable, Value> = {
    "茄子": {
    price: 5,
    num: 2
    },
    "西红柿": {
    price: 5,
    num: 2
    },
    "黄瓜": {
    price: 5,
    num: 2
    },
    };

    (Object.entries(list) as [Vegetable, Value][]).map(([key, item]) => {
    // ...
    });
    ```

    要是我的话就这么写。

    或者就直接使用 Map,这样 list.entries() 能保持 key 的类型。
    Oucreate
        29
    Oucreate  
       2019-08-28 18:04:23 +08:00
    @azh7138m 这种情形下指定为 `<T>` 是为了统一和方便吗?
    azh7138m
        30
    azh7138m  
       2019-08-28 18:55:55 +08:00   1
    @Oucreate 一般是为了更好 /多的类型检查
    xiaoming1992
        31
    xiaoming1992  
    OP
       2019-08-29 00:56:41 +08:00 via Android   1
    @lhc70000 对,我后来就这么写的,省得到里面一个一个的给 key 加 as。

    @Oucreate 对于复杂一些的对象,有类型检查和提示更方便啊,我现在恨不得所有变量都尽可能精确地描述它的类型。正常情况下 key 确实是 string,但是当我的 key 受到业余限定,只有这么确定的几种的话,用 a | b | c 来限定它们的类型会更方便。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3104 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 33ms UTC 12:39 PVG 20:39 LAX 05:39 JFK 08:39
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86