带有从未输入字段的打字稿界面

Typescript interface with never typed fields

给定以下接口,什么是有效的可分配值?

interface A {
  x: number,
  y: never
}

我所期望的是 const a: A = { x: 1 } 会起作用,但它错误地指出字段 y 是必需的。一旦我把 y 放在那里,它就说该值不可分配给 never.

我的实际用例是这样的:

interface Context<T extends MyRequest> {
  id: string;
  token: T extends AuthenticateRequest ? string : never;
}

interface AuthenticatedRequest extends MyRequest { ... }

这里我无法为 Context<MyRequest> 创建值,因为它说缺少 token

我目前的解决方法是:

type Context<T extends MyRequest> = {
  id: string;
} & (T extends AuthenticatedRequest ? {
  token: string;
} : {})

但由于显而易见的原因,这看起来很难看...

知道我如何正确地做到这一点吗?

您需要将类型设置为字符串或未定义。当永远不会为变量赋值时使用 never 类型,更多关于 here.

我认为这种方法应该可以解决您的问题。

interface A {
  x: number,
  y: string | undefined
}

Type never 是空类型的表示。这是什么意思 - 没有属于这种类型的值,这意味着如果你使用它总是编译错误。

你需要的是更高级别的条件类型,考虑:

type Context<T extends MyRequest> = T extends AuthenticateRequest ? {
  id: string;
  token: string;
} : { id: string };

这种方法消除了为 token 设置 undefined 的负担。如果需要token,那么对于T extends AuthenticateRequest它是一个字符串,如果不是,则token字段不存在。

根据@Titian Cernicova-Dragomir 和@Maciej Sikora 提到的要点,解决方法是我需要的类型定义的正确方法。 interface 不能有类型为 never 的字段,因为无法为其分配有效值,从而使接口无用。

这是我最终用于代码的内容:

type Context<T extends MyRequest> = Readonly<
  {
    logger: MyLogger;
    timestamp?: number;
  } & (T extends AuthenticatedRequest
    ? {
        token: Token;
        user: string;
      }
    : {}) &
    (T extends GameRequest
      ? {
          gameId: string;
        }
      : {}) &
    (T extends ETagRequest
      ? {
          etag: string;
        }
      : {}) &
    (T extends UnParameterizedRequest
      ? {}
      : {
          params: Record<string, string | undefined>;
        })
>;