这个 TypeScript 类型应该怎么写?

2020-04-23 09:54:21 +08:00
 rabbbit

大致过程就是检查 b 上有没有和 a 相同的属性,如果有就赋值给 a.

interface A {
  foo: string;
  bar: number;
};
const a: A = {
  foo: 'a',
  bar: 1,
};

interface B extends Partial<A> {
  [propName: string]: any;
}
const b: B = {
  foo: 'b',
  bar: 2,
  c: 3,
};

for (const key of Object.keys(a)) {
  if (b[key]) {
    a[key] = b[key]; // 报错: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'A'.
  }
}
for (const key of Object.keys(a)) {
  const k = key as keyof typeof a;
  if (b[k]) {
    a[k] = b[k]; // 报错: Type 'string | number' is not assignable to type 'never'.
  }
}

除了像下面这样给 A 加上 [propName: string] 之外还有别的解决办法吗?

interface A {
  [propName: string]: string|number;
  foo: string,
  bar: number,
};
1222 次点击
所在节点    问与答
4 条回复
blindie
2020-04-23 11:17:07 +08:00
let merged = {...a, ...b} ?
rabbbit
2020-04-23 11:28:02 +08:00
@blindie
这么写应该不行, b 上可能有 a 没有的属性.
noe132
2020-04-23 11:39:23 +08:00
a[k] = b[k] 目前是无法做到 sound 的。
本质上是 ts 无法对 union key 做 narrow 。
https://github.com/microsoft/TypeScript/issues/13995

假设你有这么一个函数,能够通过编译检查(在 ts3.4 之前是可以通过检查的)
const write = <K extends keyof A>(obj:A, key: K, value: A[K]) {
obj[key] = value
}

write(a, 'foo', 1) 是完全符合这个函数的类型声明的。

左值如果类型是个集合,那么类型是集合所有元素的交集。
以 a[key] 为例 string & number 得到的结果就是 never 。

实际上,缺少的是类似 oneof 这样的操作符,将 K 限定在集合中的某一项。

目前只能使用 (a as any)[key] = b[key] 的方式绕过。类型的健全就必须靠你自己保证。
blindie
2020-04-23 15:55:30 +08:00
@rabbbit 你既然两个都是 interface 按照 duck typing 的思想没有影响照用呗

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

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

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

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

© 2021 V2EX