输入'string |数字”不能分配给“从不”类型

时间:2019-12-14 19:08:47

标签: javascript typescript

我想将键和值从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 --initversion 3.5.1一起运行来使用默认值

谢谢。

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。不幸的是,stringnumber都没有值...因此被简化为never。糟糕!


要解决此问题,我可能会重构此代码,以便更狭窄地键入list,只允许“匹配” foobar属性,然后传递{{ 1}}的 generic 回调方法,其中对forEachfoo进行了注释,以便将barobj[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 }); 类型会与mappedlookup类型进行一点点杂耍,以产生所有可接受的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

这有意义吗?希望能有所帮助;祝你好运!

Link to code