输入'字符串| number' 不可分配到类型 'never'

Ray*_*han 5 javascript typescript

我想将键和值动态映射listobj. 但是,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;
});
Run Code Online (Sandbox Code Playgroud)

我注意到,如果我将输入[k: string]: any包含在 中interface T,错误消息就会消失。

但是,我不愿意这样做,因为我可以将其他键/值对添加到 中obj,例如obj.d = 'error'没有 TS 警告我。

另外,我很好奇为什么 TS 会给我这个错误消息,以及类型never是什么。

对于tsconfig.json,我通过运行使用默认值tsc --initversion 3.5.1

谢谢你。

jca*_*alz 6

TypeScript 3.5 关闭了一个漏洞,即未正确检查键联合上的索引访问写入。如果我有一个objtype的对象T和一个foo泛型 type的键keyof T,那么虽然您可以安全地从,like读取类型的属性,但编写这样的属性可能不安全,例如 在您的代码中,可能是,也可能是,这样写是不安全的。T[keyof T]obj[foo]const baz: T[keyof T] = obj[foo]const bar: T[keyof T] = ...; obj[foo] = bar;foo"a"bar1

漏洞被关闭的方式:如果我从键的联合中读取一个值,它会变成属性类型的联合,就像以前一样。但如果我一个值键的结合,它成为一个路口物业类型。所以说我有一个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。不幸的是,没有既是 astring又是 a number... 的值,所以它被简化为never. 哎呀。


为了解决这个问题,我可能会重构这段代码,使其list类型更窄,只允许“匹配”foobar属性,然后将forEach方法传递给通用回调,其中foobar被注释,以便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
});
Run Code Online (Sandbox Code Playgroud)

KV类型对映射查找类型进行了一些类型处理,以生成所有可接受foo/bar对的联合,您可以通过在KV定义上使用 IntelliSense 进行验证。

并且forEach()回调作用于item: { foo: K, bar: NonNullable<T[K]> }generic类型的值K extends keyof T。Soobj[foo]将被视为 type T[K],您将为其分配 a NonNullable<T[K]>根据不太合理但足够方便的规则,这是可以接受的

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

代码链接