如何在检查可选值时使 TypeScript 满意?

huangapple go评论57阅读模式
英文:

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 &amp;&amp; user?.id != null &amp;&amp; user?.name != null &amp;&amp; (
        &lt;Dashboard
          user={{
            ...user,
            imageURL: session.user.image!,
          }}
          pages={pages}
        /&gt;
      )}

But the value of the spread user still is showing a TS error

Type &#39;{ imageURL: string; id: string; name: string | null; email: string | null; }&#39; is not assignable to type &#39;{ id: string; name: string; email: string; imageURL: string; }&#39;.
  Types of property &#39;name&#39; are incompatible.
    Type &#39;string | null&#39; is not assignable to type &#39;string&#39;.
      Type &#39;null&#39; is not assignable to type &#39;string&#39;.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&#39;t nullable */
type CompleteUser = Record&lt;keyof User, NonNullable&lt;User[keyof User]&gt;&gt;;

function isCompleteUser(user: User): user is CompleteUser {
  return user?.email != null &amp;&amp; user?.id != null &amp;&amp; user?.name != null;
}

// ...

{isCompleteUser(user) &amp;&amp; (
  // TypeScript now knows that properties like `user.email` aren&#39;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.

huangapple
  • 本文由 发表于 2023年6月12日 13:41:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76453868.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定