【问题标题】:Typescript , what's the best approach for declaring a nested object with different types?Typescript ,声明具有不同类型的嵌套对象的最佳方法是什么?
【发布时间】:2017-02-20 22:10:36
【问题描述】:

我有这个 JS 对象,我想把它移植到 Typescript:

var items = [{
      style: 'activity-8',
      color: '#a32c62',
      id:    8,
      done : false,
      label : {
        short :'999 m',
        long  :'walk 999m',
        statstime  :'yesterday'
      },
      val : {
        today : {
          target   : { 
            raw : 0,
            display :"11"
          },
          achieved : {
            raw : 0,
            display :"22"
          }
        },
        yesterday : {
          target   : {
            raw : 0,
            display :"33"
          },
          achieved : {
            raw : 0,
            display :"44"
          }
        }
      }
    },{
      style: 'activity-7',
      color: '#ec575d',
      id:    7,
      done : true,
      label : {
        short :'walk 555m',
        long  :'walk 555m',
        statstime  :'yesterday'
      },
      val : {
        today : {
          target   : { 
            raw : 0,
            display :"0"
          },
          achieved : {
            raw : 0,
            display :"0"
          }
        },
        yesterday : {
          target   : {
            raw : 0,
            display :"0"
          },
          achieved : {
            raw : 0,
            display :"0"
          }
        }
      }
    }];

声明此对象类型的最佳方法是什么?我应该写下每个字段的类型吗?还是创建自定义类型?还有其他建议吗?

【问题讨论】:

  • interface per type 应该足够好了

标签: javascript typescript typescript-typings


【解决方案1】:

我不知道这是否是“最好”的方式,但我会给你举个例子来说明我这样做的方式。

我将尝试解释我的一般规则,不仅适用于嵌套对象:

对于所有基本类型的属性(字符串、数字、布尔值或某些数组),您可以将它们保留为这样,但对于从现在开始我将称为“复杂属性”的所有其他复杂属性/嵌套对象(因为它对我来说更有意义)你创建了一个接口,它将成为属性的类型。

示例:在您的情况下,val 属性是一个“复杂”属性,所以让我们从下到上拆分它。

val 属性中最小的复杂属性是target,因此您创建了一个名为 Target 的接口(或 ITarget,在 ts 中不完全是这样的约定) Target 将类似于:

interface Target {
  raw: number,
  display: string
}

您对 achieved 'complex' 属性执行相同的操作。

现在你可以上一级了。 Today 属性也是一个“复杂”属性,因此它必须具有一种可能会成为某种接口类型的类型。感谢我们之前的工作,该界面将如下所示:

interface Day {
  target: Target,
  achieved: Achieved
}

您可能想知道为什么接口被称为 Day 而不是 Today,原因是您必须找出 yesterday 'complex' 属性的类型。如您所见,您可以看到它与today 属性的类型相同,因为它内部具有相同的属性。

所以最终我们的目标val 属性将拥有自己的界面,类似于:

interface Val {
  today: Day,
  yesterday: Day
}

接下来,您对所有其他“复杂”属性执行相同的操作。

现在你可以在你的主对象中使用那些可能是一个类的对象,它的属性类型将是那些接口。

不要忘记 ts 中的界面(不仅如此)只是一个“帮助”工具,它只会帮助组织您的代码设计。大多数时候,每个接口都应该有一个实现该接口的类。这些是您将用来为这些属性实际设置一些值的类。

更新(4 年后):

现在,当我对 typescript 有更多经验时,我仍然会使用相同的策略,但我会使用 type 而不是 interface 来完成几乎相同的工作。仅当您打算拥有一个可以实现它们的类时才应使用接口(对我而言)。

【讨论】:

  • TypeScript 中的接口不必有实现它们的类。它们仅可用于组织代码和严格键入。在您的示例中,您通常可以设置一些值,例如:var target = <Target>{raw: 5, display: "somestring"}(其中Target 只是接口)。然后:var day = <Day>{target: target}
  • 我同意这个更正。即使在那种情况下,我认为您仍然在制作类似“匿名”类的东西来实现该接口。
【解决方案2】:

如果您不想提取子类型(嵌套字段作为单独的类),因为它们只在那个地方使用,那么定义类如下(来自头部的代码)

export class MyItem {
  style: string;
  color: string;
  id: number;
  done: boolean;
  label: {
    short: string,
    long: string,
    statstime: string
  };
  // ... and so on


  constructor(item: any) {        
    if(item) Object.assign(this, item); // copy item fields to "this" fields
  }
};


// and map your not-typed items table (eg readed from API) to typed
let typedItem: MyItem[] = items.map(item => new MyItem(item));

【讨论】:

    猜你喜欢
    • 2023-03-27
    • 2012-07-13
    • 2021-10-23
    • 2023-01-04
    • 1970-01-01
    • 2019-02-10
    • 1970-01-01
    • 2018-04-23
    • 2020-08-10
    相关资源
    最近更新 更多