英文:
how to make typescript happy when checking for optional values?
问题
I have this code:
{user?.email != null && user?.id != null && user?.name != null && (
<Dashboard
user={{
...user,
imageURL: session.user.image!,
}}
pages={pages}
/>
)}
But the value of the spread user still is showing a TS error:
Type '{ imageURL: string; id: string; name: string | null; email: string | null; }' is not assignable to type '{ id: string; name: string; email: string; imageURL: string; }'.
Types of property 'name' are incompatible.
Type 'string | null' is not assignable to type 'string'.
Type 'null' is not assignable to type 'string'.ts(2322)
为什么像 name
这样的值在 TypeScript 中仍然被识别为 string | null
,当检查逻辑 user?.name != null
应该使其成为非可选值?我应该如何修复这个问题?
英文:
I have this code:
{user?.email != null && user?.id != null && user?.name != null && (
<Dashboard
user={{
...user,
imageURL: session.user.image!,
}}
pages={pages}
/>
)}
But the value of the spread user still is showing a TS error
Type '{ imageURL: string; id: string; name: string | null; email: string | null; }' is not assignable to type '{ id: string; name: string; email: string; imageURL: string; }'.
Types of property 'name' are incompatible.
Type 'string | null' is not assignable to type 'string'.
Type 'null' is not assignable to type 'string'.ts(2322)
why is a value like name
still picked up by typescript as being string | null
when the checking logic user?.name != null
should make it such that the value user.name is a non-optional value? How do I fix this?
答案1
得分: 2
我大约一个月前提出了类似的问题,并在评论中得到了一个很好的答案。 TypeScript在缩小属性类型(例如使用in
运算符)时会做一些奇怪的事情。它会缩小属性的类型,就像你期望的那样,但对待父类型的方式不同。在GitHub上的相关问题中有更多信息。
这是TypeScript本身的一个限制,但可以通过定义自己的类型守卫函数来解决,告诉TypeScript如何缩小类型。
例如:
type User = {
email: string | null;
id: string | null;
name: string | null;
};
/** 与`User`相同,但属性不可为空 */
type CompleteUser = Record<keyof User, NonNullable<User[keyof User]>>;
function isCompleteUser(user: User): user is CompleteUser {
return user?.email != null && user?.id != null && user?.name != null;
}
// ...
{isCompleteUser(user) && (
// TypeScript现在知道属性如`user.email`不为空,还明白如何缩小`user`的类型
}
当然,如果你需要一个非常通用的方法,那么可能需要更多的工作或复杂性,但希望这个建议对你的具体用例有用。
英文:
I asked a similar question about a month ago and got a good answer in the comments. TypeScript does things a little strangely when narrowing property types, such as with the in
operator. It will narrow the type of the property just as you'd expect but treats the type of the parent differently. Here's the relevant issue on GitHub
This is a limitation of TypeScript itself, but it is possible to work around it by defining your own typeguard function to tell TypeScript how you intend for it to narrow a type.
For example:
type User = {
email: string | null;
id: string | null;
name: string | null;
};
/** The same as `User`, but the properties aren't nullable */
type CompleteUser = Record<keyof User, NonNullable<User[keyof User]>>;
function isCompleteUser(user: User): user is CompleteUser {
return user?.email != null && user?.id != null && user?.name != null;
}
// ...
{isCompleteUser(user) && (
// TypeScript now knows that properties like `user.email` aren't null, and also understands how to narrow the type of `user`
}
Of course, if you need a very generic approach then this can take a bit more work or complexity, but hopefully that suggestion is useful for your specific use case.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论