为什么 TypeScript `never` 类型需要问号?对于条件对象

And*_*dru 0 javascript typescript

这段代码可以很好地有条件地要求该isDirty字段成为讲座对象的一部分:

  • 如果id是 type string,我必须添加一个isDirty字段:
  • 如果id是 type number,则无法isDirty向对象添加字段。
type LectureT = LectureIdT & {
  title: string;
};

/**
 * Lecture must have an `isDirty` field if `id` is of type string, but should have no `isDirty` field if `id` if of type number.
 */
type LectureIdT =
  | { id: string; isDirty: boolean }
  | { id: number; isDirty?: never };
  

const lectureMockWithNumberId: LectureT = {
  id: 1,
  title: 'Lecture 1',
  // isDirty: false, // Uncomment to see error
};

const lectureMockWithStringId: LectureT = {
  id: "1",
  title: 'Lecture 2',
  isDirty: false,
};
Run Code Online (Sandbox Code Playgroud)

可以在此TS Playground 中使用此代码。

问题:为什么我需要问号isDirty?: never


我对此事的调查

当我删除问号isDirty?: never以获取isDirty: never它不再起作用时:

type LectureIdT =
  | { id: string; isDirty: boolean }
  | { id: number; isDirty: never };
Run Code Online (Sandbox Code Playgroud)

lectureMockWithNumberId 突然有一个打字稿错误:

Type '{ id: number; title: string; }' is not assignable to type 'LectureT'.
  Type '{ id: number; title: string; }' is not assignable to type '{ id: number; isDirty: never; } & { title: string; }'.
    Property 'isDirty' is missing in type '{ id: number; title: string; }' but required in type '{ id: number; isDirty: never; }'.
Run Code Online (Sandbox Code Playgroud)

我们在最后一句话中看到它说这isDirty是必需的。但这对我来说似乎不对!它的类型是never-> 所以它永远不是必需的,不是吗?

为什么我必须添加一个问号才能使其工作。这似乎不合逻辑。

为了理解它,我将其更改为isDirty: never | undefined

type LectureIdT =
  | { id: string; isDirty: boolean }
  | { id: number; isDirty: never | undefined };
Run Code Online (Sandbox Code Playgroud)

但令我惊讶的是,错误仍然存​​在!不是?in ega?: string等于a: string | undefined吗?

我不明白...

  1. ......为什么问号?在这里首先是必要的,而且isDirty: never;基本上是不可能的。
  2. ...为什么?当它与 一起使用时表现不同never

CRi*_*ice 5

让我一一回答这些问题:

我们在最后一句话中看到它说这isDirty是必需的。但这对我来说似乎不对!它的类型是never-> 所以它永远不是必需的,不是吗?

never就其与可选对象属性的交互方式而言,该类型没有任何独特之处。你可以认为never是相同的任何其他类型的(stringnumber,等等)。不同之处在于,与这些其他类型不同,没有可分配给 的值never。你可能会说一个类型的变量never“永远”不能被赋予一个有效的值。

但令我惊讶的是,错误仍然存​​在!不是?in ega?: string等于a: string | undefined吗?

不。考虑两个对象之间的区别:{}{a: undefined}。在一种情况下,钥匙完全丢失了。在另一个中它存在并具有值undefined。前一个对象可分配给{a?: string}但不能分配给{a: string | undefined}

这将导致:

为什么问号?在这里首先是必要的,而且isDirty: never;基本上是不可能的。

像上面的例子一样,如果你没有?在键名上包含,那么类型中需要键。关键只是存在。但是该键的值的类型是never. 因此,您无法为该键分配有效值。

所以简而言之,没有问号确实是不可能的,因为它是一个矛盾的要求:那个键必须存在,但没有值对其有效。

最后:

为什么?当它与never.

这仅仅是 的通常属性never与 的通常行为相结合的结果?。这两个特定功能的交互没有什么特别之处。

使用 的示例{isDirty?: never},这意味着以下条件之一必须为真:

  • 该对象并没有包含的isDirty关键。
  • 该对象确实包含isDirty键并且它的值是 type never

由于这两种情况中的第二种是不可能的(根据上述参数),所以只剩下第一种情况:匹配此类型的对象不得包含isDirty键。