我想将键和值从list
动态映射到obj
。但是,TS给我一个错误消息:
Type 'string | number' is not assignable to type 'never'
我不知道哪里出了问题。下面是代码片段:
interface T {
// uncomment the next line makes the error go away
// [k: string]: any
a: string;
b?: string;
c?: number;
}
const obj: T = {
a: 'something',
};
const list: Array<{
foo: keyof T;
bar: string | number;
}> = [
{ foo: 'b', bar: 'str' },
{ foo: 'c', bar: 1 },
];
list.forEach(item => {
const { foo, bar } = item;
// The error message comes from the next line
obj[foo] = bar;
});
我注意到,如果在[k: string]: any
中包含键入interface T
,错误消息就会消失。
但是,我不愿意这样做,因为我可以在不警告TS的情况下将其他键/值对添加到obj
中,例如obj.d = 'error'
。
此外,我很好奇为什么TS会给我这个错误消息,以及never
类型是什么。
对于tsconfig.json
,我通过将tsc --init
与version 3.5.1
一起运行来使用默认值
谢谢。
答案 0 :(得分:3)
TypeScript 3.5解决了index-access writes on unions of keys were not being properly checked的漏洞。如果我有obj
类型的对象T
和通用类型foo
的键keyof T
,尽管可以安全地 read T[keyof T]
中obj[foo]
类型的const baz: T[keyof T] = obj[foo]
,例如const bar: T[keyof T] = ...; obj[foo] = bar;
,写入这样的属性,例如foo
,在您的代码中,{ {1}}可能是"a"
,而bar
可能是1
,这将是不安全的。
漏洞的关闭方式:如果我从键的并集读取值,则它会像以前一样成为属性类型的并集。但是如果我将值写入键的并集,它将成为属性类型的 intersection 。假设我有一个类型为o
的对象{a: string | number, b: number | boolean}
,并且想向o[Math.random()<0.5 ? "a" : "b"]
写一些东西……写什么安全?只有适用于 o.a
和 o.b
...的事物,即(string | number) & (number | boolean)
,(当您在分配时会费力交集和归约)变成number
。您只能安全地写一个number
。
在您的情况下,交点为string & string & number
。不幸的是,string
和number
都没有值...因此被简化为never
。糟糕!
要解决此问题,我可能会重构此代码,以便更狭窄地键入list
,只允许“匹配” foo
和bar
属性,然后传递{{ 1}}的 generic 回调方法,其中对forEach
和foo
进行了注释,以便将bar
和obj[foo]
视为相同的类型:
bar
type KV = { [K in keyof T]-?: { foo: K, bar: NonNullable<T[K]> } }[keyof T]
/* type KV = {
foo: "a";
bar: string;
} | {
foo: "b";
bar: string;
} | {
foo: "c";
bar: number;
} */
const list: Array<KV> = [
{ foo: 'b', bar: 'str' },
{ foo: 'c', bar: 1 },
];
list.forEach(<K extends keyof T>(item: { foo: K, bar: NonNullable<T[K]> }) => {
const { foo, bar } = item;
obj[foo] = bar; // okay
});
类型会与mapped和lookup类型进行一点点杂耍,以产生所有可接受的KV
/ foo
对的并集可以通过使用bar
定义上的IntelliSense进行验证。
KV
回调对通用forEach()
的类型为item: { foo: K, bar: NonNullable<T[K]> }
的值起作用。因此,K extends keyof T
将被视为类型obj[foo]
,您将为其分配一个T[K]
,即acceptable according to a rule that isn't quite sound but convenient enough to be allowed。
这有意义吗?希望能有所帮助;祝你好运!