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

2019-08-27 16:33:26 +08:00
 xiaoming1992
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 类型呢?
})
5970 次点击
所在节点    JavaScript
31 条回复
geekdada
2019-08-27 16:46:51 +08:00
运行在 Runtime 里的还是 JS 代码,所以你在运行时拿到的类型是 string。
ochatokori
2019-08-27 16:49:26 +08:00
可能我不会 ts,用 ts 有很多这样类似的问题
实在不行就<Vegetable>
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
2019-08-27 16:52:16 +08:00
entries 定义是
entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];
entries(o: {}): [string, any][];
你得自己提供一个 entries 来 hack 这个 key 的类型
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
2019-08-27 17:05:29 +08:00
```
Object.entries(list).map(([key, item]: [Vegetable, { price: number; num: number }]) => {
// 可以给参数加个类型定义
});
```
xiaoming1992
2019-08-27 17:29:35 +08:00
@wawaforya 没用,提示不能将 string 赋给 Vegetable
jatai
2019-08-27 17:33:05 +08:00
什么? ts 也设置类型? 前端高手不都是用 any 吗
xiaoming1992
2019-08-27 17:33:45 +08:00
@wawaforya 没用,提示不能将 string 分配给 Vegetable 类型;

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

@azh7138m 写这样的 interface 给我的感觉好像是 js 里面重写原型方法一样,不太喜欢这样写,虽然这样确实解决问题;
xiaoming1992
2019-08-27 17:34:19 +08:00
可能因为我菜吧 /(ㄒoㄒ)/~~
azh7138m
2019-08-27 17:43:14 +08:00
@xiaoming1992 并不,他们不是一个概念,补充一个新的签名是重载,js 里面没有这个概念
xiaoming1992
2019-08-27 17:56:22 +08:00
@azh7138m 不是说`重载`必须要将`函数定义`紧挨着`interface`写吗?还可以半中间插进来?王自如觉得很 awesome👍
azh7138m
2019-08-27 18:03:22 +08:00
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
2019-08-27 18:27:08 +08:00
@xiaoming1992 , 我这边可以的啊,难道是版本问题?我 typescript 的版本是 3.5.3

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


@azh7138m 感谢,等我吃完饭回去看看。
optional
2019-08-27 18:45:41 +08:00
@xiaoming1992 不会报错
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
2019-08-27 23:08:09 +08:00
@zbinlin .如果这么搞的话,那什么 map foreach 什么的都要声明。。。
zbinlin
2019-08-28 00:12:07 +08:00
@xiaoming1992 不需要呀,为什么需要,map, foreach 这些已经能正确地推导出数组里的类型的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/595578

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX