【问题标题】:Why does any convert to a more specific type without a cast为什么任何转换为​​更具体的类型而不进行强制转换
【发布时间】:2016-10-21 13:07:30
【问题描述】:

在尝试找出将 http 调用的 json 响应转换为 TypeScript 类/接口的最佳方法时,我在 stack overflow 上找到了此代码片段。

getTeachers(): Observable<Teacher> {
  return this.http.get('someUrl')
    .map((res: Response) => res.json())
}

看起来不错,但我不明白它是如何工作的。

  • 为什么没有转换语法将res.json() 的通用any 类型转换为更具体的类型Teacher
  • 为什么建议 Teacher 是 TypeScript interface 而不是 TypeScript class

【问题讨论】:

    标签: typescript


    【解决方案1】:

    如果你看一下 Observable 的签名:

    export declare class Observable<T> implements Subscribable<T> {...}
    

    你可以看到它需要参数 TSomeThing&lt;SomeType&gt; 被称为 generic 并引用 TypeScript Hadnbook,如果您查看泛型标识函数:

      function identity(arg: any): any {
        return arg;
      }
    

    虽然使用 any 肯定是通用的,因为它将接受 arg 类型的任何和所有类型,但我们实际上正在丢失函数返回时该类型是什么的信息。如果我们传入一个数字,我们所拥有的唯一信息就是可以返回 any 类型。

    相反,我们需要一种捕获参数类型的方法,以便我们也可以使用它来表示返回的内容。在这里,我们将使用 类型变量,这是一种特殊类型的变量,适用于类型而不是值。

      function identity<T>(arg: T): T {
        return arg;
      }
    

    你可以定义getTeachers(): any {},以防你不关心这个函数返回什么(或者你不知道)。它会起作用,但假设几个月后你正在升级你的应用程序,你制作了一个漂亮的新组件,它需要调用getTeachers()。它会返回什么? 任何。凉爽的。 =digs-through-months-old-code= 重复 N 次 (;

    但是既然你知道函数是做什么的(返回 http.method,它是一个 Observable)你可以写getTeachers(): Observable&lt;any&gt;{}。 ...闪亮的新组件;致电getTeachers()。它会返回什么? 可观察。很好,我可以订阅它,并获得 Any。好的,我可以使用它...

    在大多数情况下/示例中,这就足够了。但你可以更具体。 如果您知道 this.http.get('someUrl') 返回的内容(假设它是响应正文中的 JSON 对象,标头 'Content-Type' 设置为 'application/json' - 并且您使用 Respone.json() 来提取它),你可以写getTeachers(): Observable&lt;{}&gt;{}。现在我们知道它是一个对象

    最后,如果您知道该对象将具有与接口 Teacher 相同的属性,您可以这样定义您的函数:

      getTeachers(): Observable<Teacher> {}
    

    为什么没有转换语法将通用的任何类型的 res.json() 转换为更具体的类型 Teacher?

    这里没有转换,数据保持不变。您只是通过将其声明为特定类型来提供该数据的“描述”。根据您所了解的信息。没有魔法(:。

    为什么建议 Teacher 是 TypeScript 接口而不是 TypeScript 类?

    在此上下文中,Teacher 可以是类或接口。在这里没关系,TypeScript 将从类或接口中读取信息。如果您不会在任何其他上下文中使用 Teacher ,那么您将创建一个接口(它不会编译为 JavaScript,因此代码更少)。如果您在其他地方使用它,例如如果您需要Teacher 的实例:

      let new_guy = new Teacher();
    

    那么你应该将 Teacher 声明为一个类。

    【讨论】:

    • 由于Observable 将泛型参数定义为Teacher.map 函数是否推断映射以将 json 结果映射为typeof(Teacher)
    • @Sasxa 即使数据保持不变,在大多数语言中,在将泛型类型(如任何)转换为更具体的类型(如教师)时,您都需要显式转换。
    • @TomDeseyn 对于具有real 静态类型的语言来说是正确的。 Typescript 有点不同,请参阅here 以获得对类型系统概念的更深入解释
    【解决方案2】:

    我将根据@Sasxa 的回答和我通过其他渠道收到的输入来回答我自己的问题。我的重点是向有使用强类型语言背景的人解释它。

    任何

    首先,你可能认为any 是所有类型的超类型。就是这样! Any 可用于引用任何实例。

    TypeScript 规范还定义了 any 可分配 TO 所有类型。这是根据定义,其基本原理可能是 javascript 互操作。来自规范:

    "Any 类型是所有类型的超类型,可分配给和 来自所有类型。”

    any 作为通用参数

    静态类型语言也有“协变”和“逆变”的概念。当泛型参数用作输出类型时,它可以替换为基本类型(协方差)。例如:'iterator of string'可以赋值给'iterator of object'。当泛型参数用作输入类型时,它可以替换为派生类型(逆变)。

    因为any 可以在所有类型之间分配,所以对于参数化类型也是如此。因此Observable&lt;Teacher&gt; 可以与Observable&lt;any&gt; 相互转换。

    类型断言

    TypeScript 类型断言可用于更改实例的感知类型。由于 TypeScript 是一种结构类型语言,类型描述实例的结构,但实例本身不知道类型(在断言中使用)。在声明类型时,我们不应该使用具有除实例之外的其他成员的类型。以res.json() 为例,我们不希望该类型具有方法成员,因为它是通过http get 调用检索的数据。类型是类还是接口都没有关系。

    【讨论】:

      猜你喜欢
      • 2011-12-28
      • 2015-08-17
      • 1970-01-01
      • 2019-12-04
      • 1970-01-01
      • 1970-01-01
      • 2019-03-25
      • 2021-05-08
      • 2015-05-24
      相关资源
      最近更新 更多