字符串不能用于索引类型“T”

Dam*_*yer 5 typescript

我在 TypeScript 中启用了严格模式,并且在设置对象索引时遇到问题,即使我已经在其类型上设置了字符串索引器。

禁用 noImplicitAny 可以解决此问题,但我宁愿不这样做。

我仔细研究了这个答案,但这些建议没有取得成功。

type stringKeyed = { [key: string]: any }
type dog = stringKeyed & { name: string }
type cat = stringKeyed & { lives: number }

function setLayerProps<T extends dog | cat>(
    item: T,
    props: Partial<T>
) {
    if (item) {
        Object.entries(props).forEach(([k, v]) => {
                item[k] = v; // Error: Type 'string' cannot be used to index type 'T'.ts(2536)

        });
    }
}


let d = { name: 'fido' } as dog;
let c = { lives: 9 } as cat;
setLayerProps(d, { name: 'jim' })
setLayerProps(c, { lives: --c.lives })
Run Code Online (Sandbox Code Playgroud)

谁能看到我做错了什么吗?

谢谢!

ars*_*012 7

这是#30769的效果,也是已知的重大更改。我们之前允许这样疯狂的事情,没有错误:

function foo<T extends Record<string, any>>(obj: T) {
    obj["s"] = 123;
    obj["n"] = "hello";
}
let z = { n: 1, s: "abc" };
foo(z);
foo([1, 2, 3]);
foo(new Error("wat"));
Run Code Online (Sandbox Code Playgroud)

一般来说,约束 Record<string, XXX> 实际上并不确保参数具有字符串索引签名,它只是确保参数的属性可分配给类型 XXX。因此,在上面的示例中,您可以有效地传递任何对象,并且该函数可以写入任何属性而无需任何检查。

在 3.5 中,我们强制规定,只有当我们知道相关对象具有索引签名时,您才能写入索引签名元素。因此,您需要对克隆功能进行一些小更改:

function clone<T extends Record<string, any>>(obj: T): T {
    const objectClone = {} as Record<string, any>;

    for (const prop of Reflect.ownKeys(obj).filter(isString)) {
        objectClone[prop] = obj[prop];
    }

    return objectClone as T;
}
Run Code Online (Sandbox Code Playgroud)

通过此更改,我们现在知道 objectClone 有一个字符串索引签名。

所以你的代码应该是

function setLayerProps<T extends dog | cat>(
    item: T,
    props: Partial<T>
) {
    if (item) {
        Object.entries(props).forEach(([k, v]) => {
            (item as dog | cat)[k] = v;

        });
    }
}
Run Code Online (Sandbox Code Playgroud)

参考:https ://github.com/microsoft/TypeScript/issues/31661#issuecomment-497138929