如何在Angular中有条件地更改Typescript中的类型?

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

How to conditionally change the type in typescript in angular?

问题

I am making an angular component dependent on the API Response.
我正在创建一个Angular组件,其依赖于API响应。

I am getting these from an API Response.
我从API响应中获取这些信息。

  1. When there's no error

  2. 当没有错误时

    Data : {
    Status: string;
    Data: number;
    }

  3. When there's an Error I am getting this.

  4. 当出现错误时,我会得到以下内容。

    Data : {
    Status: string;
    Error: string;
    Message: string;
    }

As we can see Status is common I tried implementing this.
正如我们所看到的,状态(Status)是共同的,我尝试实现了这一点。

interface ApiModel {
Status: string;
}

interface DataModel extends ApiModel {
Data: number;
}
interface ErrorModel extends ApiModel {
ErrorCode: string;
Message: string;
}

I am trying to use the typescript to give it my variable an option where it can be either one and then pass that type in its response too.
我试图使用TypeScript为我的变量提供一个选项,它可以是其中之一,然后将该类型传递给其响应。

I don't want to use optional type here hence I am facing these issues.
我不想在这里使用可选类型,因此我面临了这些问题。

What I am expecting is,
我期望的是,

if (typeof Response === DataModel){
if (Response instanceof DataModel) {
Response: DataModel;
// Other code
}
else {
Response: ErrorModel;
// Other code
}

The API call here
这里的API调用

getReminder(reminderId: number) {
const API_URL = ${this.BASE_URL}users/${this.userId}/reminders/${reminderId};
return this.httpClient.get<ErrorModel | DataModel>(API_URL, this.options).pipe(
map((data: ErrorModel | DataModel) => {
return data;
})
);
}

英文:

I am making an angular component dependent on the API Response.
I am getting these from an API Response.

  1. When there's no error

    Data : {
    Status: string;
    Data: number;
    }

  2. When there's an Error I am getting this.

    Data : {
    Status: string;
    Error: string;
    Message: string;
    }

As we can see Status is common I tried implementing this.

interface ApiModel {
  Status: string;
}

interface DataModel extends ApiModel {
  Data: number;
}
interface ErrorModel extends ApiModel {
  ErrorCode: string;
  Message: string;
}

I am trying to use the typescript to give it my variable an option where it can be either one and then pass that type in it's response too.

I don't want to use optional type here hence I am facing these issues.

What I am expecting is,

if (typeof Response === DataModel){
Response: DataModel;
// Other code
}
else {
Response:ErrorModel;
// Other code
}

The API call here

getReminder(reminderId: number) {
    const API_URL = `${this.BASE_URL}users/${this.userId}/reminders/${reminderId}`;
    return this.httpClient.get&lt;ErrorModel | DataModel&gt;(API_URL, this.options).pipe(
      map((data: ErrorModel | DataModel) =&gt; {
        return data;
      })
    );
  }

答案1

得分: 2

根据你的请求,以下是代码的翻译:

接口 DataModel {
  状态: 'success';
  数据: number;
}

接口 ErrorModel {
  状态: 'error';
  错误码: string;
  消息: string;
}

类型 ApiModel = DataModel | ErrorModel;

函数 foo(响应数据: ApiModel): void {
  如果 (响应数据.状态 === 'success') {
    控制台.记录(响应数据.数据);
  } 否则 {
    控制台.记录(响应数据.错误码);
  }
}

foo({状态: 'success', 数据: 10});
接口 ApiModelBase {
  状态: string;
}

接口 DataModel 扩展自 ApiModelBase {
  数据: number;
}
接口 ErrorModel 扩展自 ApiModelBase {
  错误码: string;
  消息: string;
}

类型 ApiModel = DataModel | ErrorModel;

函数 isDataModel(响应: ApiModel): 响应 是 DataModel {
  返回 (响应 as DataModel).数据 !== undefined;
}

函数 foo(响应数据: ApiModel): void {
  如果 (isDataModel(响应数据)) {
    控制台.记录(响应数据.数据);
  } 否则 {
    控制台.记录(响应数据.错误码);
  }
}

foo({状态: 's1', 数据: 10});

请注意,我已经将代码中的注释和链接保留在原文中。

英文:

To my eyes your model looks like Discriminated union

If you model it correctly, the compiler will be able to do the narrowing without extra code.
Note that:

  • there is no need to downcast after the type predicate check
  • discriminator checks one of two union members - if it is not DataModel, the compiler can deduce that it is an ErrorModel
interface DataModel {
  Status: &#39;success&#39;;
  Data: number;
}

interface ErrorModel {
  Status: &#39;error&#39;
  ErrorCode: string;
  Message: string;
}

type ApiModel = DataModel | ErrorModel;

function foo(responseData: ApiModel): void {
  if (responseData.Status === &#39;success&#39;) {
    console.log(responseData.Data);
  } else {
    console.log(responseData.ErrorCode);
  }
}

foo({Status: &#39;success&#39;, Data: 10});

If you cannot model your data as a discriminated union, you still model the response as a union, but you will have to resort to a [Type predicate] to verify which member of the union you are dealing with

interface ApiModelBase {
  Status: string;
}

interface DataModel extends ApiModelBase {
  Data: number;
}
interface ErrorModel extends ApiModelBase {
  ErrorCode: string;
  Message: string;
}

type ApiModel = DataModel | ErrorModel;


function isDataModel(response: ApiModel): response is DataModel {
  return (response as DataModel).Data !== undefined;
}

function foo(responseData: ApiModel): void {
  if (isDataModel(responseData)) {
    console.log(responseData.Data);
  } else {
    console.log(responseData.ErrorCode);
  }
}

foo({Status: &#39;s1&#39;, Data: 10});

TS Playground

答案2

得分: 0

可以使用这个模式来测试接收到的类型,进行内联类型转换:

function isDataModel(response: ApiModel): response is DataModel {
  return (response as DataModel).Data !== undefined;
}

// 当`responseData`是从API收到的实际响应时
const responseData: ApiModel = ...;

if (isDataModel(responseData)) {
  // 响应是DataModel类型
  const dataResponse: DataModel = responseData;
  // DataModel的其他代码
} else {
  // 响应是ErrorModel类型
  const errorResponse: ErrorModel = responseData;
  // ErrorModel的其他代码
}
英文:

You could use this pattern to test what type you are receiving with some inline type conversion:

function isDataModel(response: ApiModel): response is DataModel {
  return (response as DataModel).Data !== undefined;
}

// when `responseData` is the actual response received from the API
const responseData: ApiModel = ...;

if (isDataModel(responseData)) {
  // The response is of type DataModel
  const dataResponse: DataModel = responseData;
  // Other code for DataModel
} else {
  // The response is of type ErrorModel
  const errorResponse: ErrorModel = responseData;
  // Other code for ErrorModel
}

答案3

得分: 0

你尝试过在 TypeScript 中使用联合类型来实现你想要的吗?

type YourResponse = DataModel | ErrorModel;

以下是可能的示例用法:

const response: YourResponse = {
  Status: "Success",
  Data: 100,
};

if (response instanceof DataModel) {
  这是一个 DataModel
} else if (response instanceof ErrorModel) {
  这是一个 ErrorModel
}
英文:

Did you try using Union type in TypeScript to achieve what you want?

type YourResponse = DataModel | ErrorModel;

This could be the example usage:

const response: YourResponse = {
  Status: &quot;Success&quot;,
  Data: 100,
};

if (response instanceof DataModel) {
  It is a DataModel.
} else if (response instanceof ErrorModel) {
  It is an ErrorModel.
}

答案4

得分: -1

import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

isDataModel(response: ApiModel): response is DataModel {
  return response.Status === 'success';
}

getReminder(reminderId: number): Observable<DataModel | ErrorModel> {
  const API_URL = `${this.BASE_URL}users/${this.userId}/reminders/${reminderId}`;
  return this.httpClient.get<ApiModel>(API_URL, this.options).pipe(
    map((response: ApiModel) => {
      if (isDataModel(response)) {
        const responseData: DataModel = response as DataModel;
        return responseData;
      } else {
        // ErrorModel handling
        const errorResponse: ErrorModel = response as ErrorModel;
        throw errorResponse;
      }
    })
  );
}
interface ApiModel {
  Status: string;
}

interface DataModel extends ApiModel {
  Data: number;
}

interface ErrorModel extends ApiModel {
  ErrorCode: string;
  Message: string;
}
英文:
import { HttpClient, HttpErrorResponse } from &#39;@angular/common/http&#39;;
import { Observable } from &#39;rxjs&#39;;
import { map } from &#39;rxjs/operators&#39;;

isDataModel(response: ApiModel): response is DataModel {
  return response.Status === &#39;success&#39;;
}

getReminder(reminderId: number): Observable&lt;DataModel | ErrorModel&gt; {
  const API_URL = `${this.BASE_URL}users/${this.userId}/reminders/${reminderId}`;
  return this.httpClient.get&lt;ApiModel&gt;(API_URL, this.options).pipe(
    map((response: ApiModel) =&gt; {
      if (isDataModel(response)) {
        const responseData: DataModel = response as DataModel;
        return responseData;
      } else {
        // ErrorModel handling
        const errorResponse: ErrorModel = response as ErrorModel;
        throw errorResponse;
      }
    })
  );
}

Interfaces can be as earlier

interface ApiModel {
  Status: string;
}

interface DataModel extends ApiModel {
  Data: number;
}

interface ErrorModel extends ApiModel {
  ErrorCode: string;
  Message: string;
}

Let me know if there are any issues or clarifications needed.

huangapple
  • 本文由 发表于 2023年7月24日 19:07:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76753858.html
匿名

发表评论

匿名网友

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

确定