【问题标题】:Angular 9 typecast issue after http responsehttp响应后的Angular 9类型转换问题
【发布时间】:2020-08-20 10:00:31
【问题描述】:

我有一个组件,它在初始化时从 api 检索学生信息。 这是我的 cmponent-version1

上的 onIniti 代码
ngOnInit(): void {
    if(!this.student) {
      this.studentsService.getStudentDetail(this.id).subscribe(
        (response: Student) => {
          this.student = response;
        },
        error => console.log(error)
      )
    }
  }

这是我的student-service-version1

里面的函数
getStudentDetail(id: number): Observable<Student> {
    return this.httpClient.get<Student>(`${this.studentsUrl}${id}/`, this.baseService.httpOptions);
  }

一切正常。现在,仅出于教学目的(我是 javascript/typescript 的新手),我想重构我的服务以使用单个 get 函数,该函数在不带参数调用时返回学生列表,而是在使用特定 ID 调用时返回学生详细信息。 这是students-service-version2

getStudents(id?: number): Observable<Student[]> {
    if(id)
      return this.httpClient.get<Student[]>(`${this.studentsUrl}${id}/`, this.baseService.httpOptions);
    else
      return this.httpClient.get<Student[]>(this.studentsUrl, this.baseService.httpOptions);
  }

鉴于我的函数的签名声明它返回一个可观察的学生数组,在我的组件中,我需要一种从 Student[]Student 的类型转换。我就是这样做的: 组件版本2

ngOnInit(): void {
    if(!this.student) {
      this.studentsService.getStudents(this.id).subscribe(
        (response: Student[]) => {
          this.student = response[0] as Student;
        },
        error => console.log(error)
      )
    }
  }

这不起作用,所以在初始化之后,student var 保持未定义。我不明白为什么,一切对我来说似乎都是正确的(尽管这种重构不是一个好主意。同样,我只是想了解背后的错误) 我也试试 this.student = response.pop() as Student; 同样的结果,不工作。

【问题讨论】:

  • response 长什么样子?
  • 你在哪里检查学生变量?因为您的分配student = response[0] 在订阅中,这是异步的
  • 使用调试器。在 onInit() 之后使用版本 #1,学生没问题,使用版本 #2 会导致未定义
  • @brk console.log(response) 打印:{id: 18, cf: "ciaciacia", first_name: "Paolo", last_name: "Bianchi", date_of_birth: "2020-05-01 ", ...} 果然是学生信息

标签: javascript arrays angular typescript observable


【解决方案1】:
ngOnInit(): void {
if(!this.student) {
  this.studentsService.getStudents(this.id).subscribe(
    (response: Student[]) => {
      // Hope, this.student will have type as any, public student: any
      this.student = !this.id ? response[0] as Student : response as Student[];
    },
    error => console.log(error)
  )
}

}

始终尝试返回一个数组以避免冲突。在上面的代码中, 三元运算符会做你的工作。因为,如果你有一个 id 表示您正在询问特定的学生信息,否则您 正在询问所有学生记录。

【讨论】:

    【解决方案2】:

    您的服务现在应该对您大喊大叫,因为您在对编译器撒谎...您的返回类型不是 Observable&lt;Student[]&gt; 而是 Observable&lt;Student[] | Student&gt;...我不同意一个函数的原理对于 single 和 list 都可以得到,但你可以强制它成为一个在单例中的列表...

    return this.httpClient.get<Student>(`${this.studentsUrl}${id}/`, this.baseService.httpOptions).pipe(
      map(student => [student])
    );
    

    如果不是数组,则没有类型转换会将其转换为数组。如果你愿意的话,你需要明确地把它变成一个数组。

    【讨论】:

    • 对不起,这个问题是一个错字。我已经纠正了。现在它看起来像在我的编辑器中,并且 getStudent() 以两种方式返回一个学生数组。
    • 如果响应实际上是一组学生?你有响应本身的示例 JSON 吗?在get&lt;Type&gt; 中插入一个类型实际上并不会改变响应的任何内容。它只是告诉智能感知它是什么。类型转换除了告诉编译器发生了什么之外,在打字稿中什么也不做。编译器不能不知道你是否在撒谎
    • 你的 sn-p 成功了,所以我接受它作为答案。尽管如此,我还是不明白为什么我需要一张地图,而我不能只从 Student 数组中提取 Student 类型的元素
    • 因为实际的响应不是数组,所以如果它们不是数组,则不能将它们类型转换为数组。
    • 不仅如此,类型转换只是为了开发人员。打字稿无法提前知道您的服务器实际响应的内容。它不能自动将事物塑造成你所说的你希望它们成为的样子。
    【解决方案3】:

    重载方法的签名如下:

    class StudentService {
      get(id: number) | Observable<Student>;
      get(): Observable<Student[]>;
      get(id: number | undefined): Observable<Student[]> | Observable<Student> {
        if(id !== undefined)
          return this.httpClient.get<Student[]>(`${this.studentsUrl}${id}/`, this.baseService.httpOptions);
        else
          return this.httpClient.get<Student>(this.studentsUrl, this.baseService.httpOptions);
      }
    } 
    

    请注意如何重命名该方法以使其在任何一种情况下都有意义,返回类型如何与 id 参数的存在相关联,以及如何修改检查以适应 0 作为有效身份证件。

    【讨论】:

    • 是的,我什至没有考虑 0 的情况。谢谢
    猜你喜欢
    • 1970-01-01
    • 2020-06-18
    • 1970-01-01
    • 1970-01-01
    • 2020-07-11
    • 1970-01-01
    • 2018-02-17
    • 1970-01-01
    • 2011-11-17
    相关资源
    最近更新 更多