当我写一些代码时,我有一些这样的问题:
function getObjectKeys<T extends object>(object: T) {
return Object.keys(object) as (keyof T)[]
}
const props = {
propA: 100,
propB: 'text'
}
const store = { ...props }
getObjectKeys(props).forEach((key) => {
store[key] = props[key]
})
报告一些错误:
const store: {
propA: number;
propB: string;
}
Type 'string | number' is not assignable to type 'never'.
Type 'string' is not assignable to type 'never'.
当我这样写的时候:
getObjectKeys(props).forEach((key) => {
if (key === 'propA') {
store[key] = props[key]
} else if (key === 'propB'){
store[key] = props[key]
} else {
store[key] = props[key]
}
})
它可以工作,但不是很好。如何解决这些问题?
潜在的问题是microsoft/TypeScript#30769在对对象属性的联合赋值时实现的类型安全改进。当面对像
这样的代码时type PropKeys = keyof typeof props; // "propA" | "propB"
getObjectKeys(props).forEach((key: PropKeys) => {
store[key] = ... // what is allowable here
});
编译器看到key
是联合类型"propA" | "propB"
。它不知道key
是"propA"
还是"propB"
,所以为了安全起见,它只允许赋值,不管结果是什么都可以。这意味着属性类型的交集。因为这些类型是number
和string
,所以交集是number & string
,这就简化为不可能的never
类型,因为没有值同时具有这两种类型。所以编译器不能允许任何赋值给store[key]
。
在这种情况下的解决方法是将联合类型的值替换为约束于联合的泛型类型的值。当键或对象是泛型时,不会应用来自microsoft/TypeScript#30769的安全检查。这是潜在的不安全,但它是允许的,正如在这条评论中提到的。它看起来像这样:
getObjectKeys(props).forEach(<K extends PropKeys>(key: K) => {
store[key] = props[key]; // okay
})
编译时没有错误,并且是尽可能接近类型安全的。如果将赋值值更改为绝对不安全的值,例如将props.propA
复制到store[key]
,则会再次收到警告:
getObjectKeys(props).forEach(<K extends PropKeys>(key: K) => {
store[key] = props.propA; // error!
})
Playground链接到代码
您应该避免在TS中使用object
,这不是推荐或理想的解决方案,而是使用Record<..., ...>
。
目标对象store
必须是Record<string, any>
才能使你的代码工作,因为TS不知道哪些属性将提前添加。
function getObjectKeys<T extends Record<string, any>>(object: T) {
return Object.keys(object) as (keyof T)[]
}
const props = {
propA: 100,
propB: 'text'
}
const store: Record<string, any> = {}
getObjectKeys(props).forEach((key) => {
store[key] = props[key]
})
操场上联系