如何从类型化对象中排除特定的键值?



我有以下的检查逻辑,检查从一个特定的类型和一个组件得到的道具有2种类型的道具强制和可选

在当前的情况下,重量道具是可选的,问题是isCurrentType函数返回2个类型和el。重量是错误的,因为另一种类型没有重量我有一种感觉,我有点过于复杂的架构。

const CurrentView: React.FC<Props> = () => {
cosnt items = useAppSelector((state) => state.data.itemsFromDB);
type TypeA = {id:string; equipment:string; weight:string}
type TypeB = {id:string; equipment:string;}
export const isCurrentType = (item: any): item is TypeA | TypeB => {
return ['typeA', 'TypeB'].includes(item?.itemType);
};
const renderItems = items.map((el) => {
if (isCurrentType(el)) {
return (
<Component
id={el.id}
equipment={el.equipment}
weight={el.weight/*Erorr on el.weight*/ ? el.weight : undefined}       
/>
);
}})
return(<div>{renderItems}</div>)
}

编译器不会让你访问联合类型的属性,除非联合的每个成员在该键处都有一个已知的属性:

type U = { id: string, weight: string } | { id: string }
declare const u: U;
u.id.toUpperCase(); // okay
u.weight // error!   Property 'weight' does not exist on type '{ id: string; }'.
这是因为TypeScript中的对象类型不是密封的精确的。从技术上讲,{id: string}类型的值可能包含像weight这样的额外属性(尽管过多的属性检查会使这变得复杂):
const something = { id: "abc", weight: 123 }
const oops: U = something; // this is accepted

编译是因为someThing匹配{id: "abc"},并且number类型的weight不会使其无效。您假设类型为U的值要么具有string值的weight属性,要么完全没有weight属性。但是,赋值const oops: U = something表明,这种假设在技术上是不正确的,这就是您遇到的问题。


有几种方法可以解决这个问题。您可以显式地禁止在第二个联合元素上使用weight,方法是将其设置为可选属性,其值为不可能的never类型:

type U = { id: string, weight: string } | { id: string, weight?: never } 
// now weight is known in every member of the union --> ^^^^^^^^^^^^^^
declare const u: U;
u.weight // okay, string | undefined
const something = { id: "abc", weight: 123 }
const oops: U = something; // error! 

现在可以了;已知u.weight的类型为stringundefined。编译器捕获somethingoops的赋值,因为现在{id: string, weight: number}不再可赋值给U。也就是说,现在可以正确地假设weight存在并且string值,或者不存在(或多或少)。


一种侵入性较小的修复方法是使用窄化技术,该技术与您所做的假设相同,但在技术上是不正确的。编译器将使用in运算符作为一种方法,将联合类型的值缩小到已知具有checked属性的联合成员:

"weight" in u ? u.weight : undefined // okay, string | undefined

"weight" in u检查为true时,编译器假定u.weight的类型为string,因此允许对u.weight求值。这种窄化在技术上是不安全的,所以只有在相对确定会发生oops赋值的情况下,这种方法才合适。毕竟,编译时仍然没有错误:

const something = { id: "abc", weight: 123 }
const oops: U = something; // still no error 

但是只要你的代码不做这样的事情,in-operator窄化应该可以为你工作。

Playground链接到代码

最新更新