【问题标题】:Restrictions on number using typescript使用打字稿的数量限制
【发布时间】:2021-04-17 19:16:03
【问题描述】:

我正在尝试定义一个使用具有两个属性的泛型的类型/接口(在本练习中不关心哪个):

interface Response<T> {
  status: number;
  data: T | undefined;
}

我要捕获的限制是当状态!== 200 时,数据必须是未定义的。当 status === 200 时,data 必须是 T。这样,在我检查以确保 response.status 为 200 之后,我不必总是检查 response.data 是否未定义:

if (response.status === 200 && response.data) {
  // Wouldn't it be nice it TS just knew that response.data is
  // not undefined without having to check it explicitly?
}

到目前为止,我有这个:

interface IOkResponse<T> {
  status: 200;
  data: T;
}

interface IErrorResponse {
  status: number;
  data: undefined;
}

type Response<T> = IOkResponse<T> | IErrorResponse;

当然,由于 IErrorResponse 不会将状态限制为不是 200 的数字,因此这是行不通的。我将如何添加该限制?

【问题讨论】:

  • 不理想,但您可以列出您支持的所有其他状态。只有大约 60 个,所以不是世界末日。
  • 如果没有更好的方法,这就是我打算做的:)
  • TypeScript 不支持否定类型(它曾经是worked on 并最终被放弃),所以没有办法说“number &amp; not 200”。如果您的类型是 string "200",则可以通过编程方式生成从 "100""599" 的所有三位字符串文字,并在 TS4.1 中排除 "200",但如果您想要数字文字最好只在手动联合中枚举有效代码。如果您对字符串文字版本感兴趣,请告诉我。
  • @jcalz 顺便说一句,我怎么能联系到你?我给你写了一封电子邮件,但我不确定你是否阅读了它
  • @KMehta 请参阅this playground link 了解更多信息

标签: typescript typescript-generics


【解决方案1】:

虽然@captain-yossarin 有一个更通用的功能解决方案(这就是我正式接受它作为答案的原因),但这是我采用的解决方案......

export enum StatusCode {
  OK = 200,
  NoContent = 204,
  NotModified = 304,
  BadRequest = 400,
  Unauthorized = 401,
  // Other status codes that the client cares about go here
}

interface IOkResponse<T> {
  statusCode: StatusCode.OK;
  data: T;
}

interface IErrorResponse {
  statusCode: Exclude<StatusCode, StatusCode.OK>;
  data: undefined;
}

export type ApiResponse<T> = IOkResponse<T> | IErrorResponse;

这个负责创建我的ApiResponse&lt;T&gt; 对象实例的函数工作正常,因为TypeScript 会将number 隐式转换为StatusCode

async function parseResponse<T>(response: Response): Promise<ApiResponse<T>> {
  const text = await response.text();
  const statusCode = response.status;
  if (statusCode === StatusCode.OK) {
    const data = JSON.parse(text) as T;
    return { statusCode, data };
  }
  return { statusCode, data: undefined };
}

现在,当statusCode === 200 时,我可以使用我的ApiResponse&lt;T&gt; 对象而无需进行不必要的未定义检查。

const ar1: ApiResponse<Test> = { statusCode: 200, data: { value: 5 } };
if (ar1.statusCode === StatusCode.OK) {
  console.log(ar1.data.value);
}

【讨论】:

    【解决方案2】:

    我同意@jcalz 的观点,没有什么俗套的方法可以做到这一点。 您可以做的一件事是类型保护。它将为您的代码类型提供安全性,但您应该为此付出代价——函数开销。

    
    interface IOkResponse<T> {
      status: 200;
      data: T;
    }
    
    interface IErrorResponse {
      status: number;
      data: undefined
    }
    
    type Result<T> = IOkResponse<T> | IErrorResponse;
    
    const isOK = <T,>(arg: Result<T>): arg is IOkResponse<T> => arg.status === 200
    
    const foo = <T,>(arg: Result<T>) => {
      if (isOK(arg)) {
        const y = arg //  IOkResponse<T> 
      } else {
        const z = arg // IErrorResponse;
      }
    }
    

    【讨论】:

      【解决方案3】:
      class Response<T> {
          status: number = null;
          get data(): T {
              return this.status === 200 && this.data || undefined;
          }
      }
      

      尝试这样的事情怎么样?如果状态=== 200,数据将是未定义的

      【讨论】:

        猜你喜欢
        • 2017-01-04
        • 1970-01-01
        • 2019-06-02
        • 2019-11-19
        • 1970-01-01
        • 2018-06-14
        • 1970-01-01
        • 1970-01-01
        • 2020-05-22
        相关资源
        最近更新 更多