如何使用TypeScript条件类型表示变量JSON数据?

时间:2019-08-30 21:36:59

标签: typescript typescript-generics

我从后端获取JSON数据(我对此有一些控制权,因此如果有帮助,可以在某种程度上进行更改),如下所示:

{
  "attributes": [
    {
      "type": "string",
      "value": "foobar"
    },
    { 
      "type": "number",
      "value": 1234
    },
    {
      "type": "annotated_string",
      "value": {
        "value": "barfoo",
        "comment": "This is a comment"
      }
    }
  ]
}

我想找到一个可以表示此类型的TypeScript类型。我读到有关条件类型的信息,它们似乎与问题的一部分相匹配。 value取决于type字段的类型(实际上是其值)。

我想到了这个

type AttributeGroup = {
  attributes: Attribute[] // <-- This requires a generic parameter
}

enum AttributeType { "string" , "number", "annotated_string"}

type Attribute<T extends AttributeType> = {
  type: T;
  value: ExtractMyParameter<T>;
}

type AnnotatedString = {
  value: string;
  comment: string;
}

type ExtractMyParameter<T> =
  T extends AttributeType.number ? number :
  T extends AttributeType.string ? string :
  T extends AttributeType.annotated_string ? AnnotatedString :
    never;

let attr: Attribute<AttributeType.string> = { // <-- Ugly to have to define it twice
  type: AttributeType.string, // <-- I don't want to do this but instead: type: "string"
  value: "1234"
}

Try in Typescript Playground

在TypeScript中是否有一种很好的方式将数据表示为类型? 上面的代码由于各种原因而无法正常工作:

  • Attribute[]缺少Type参数,但是数组的每个元素都可能具有不同的Type。我想这可以通过Union类型以某种方式解决?
  • 在显式创建实例时必须专门定义Attribute是很丑陋的(这仅用于测试,因此还不错)

我认为必须有更好的方法来处理此类数据。

1 个答案:

答案 0 :(得分:1)

值得注意的是,除非您创建这些属性值(您可能会这样做),否则键入不会严格帮助您。否则,您可能会陷入一种虚假的安全感!

您应该能够使用映射类型来实现用例(在这种情况下,这只是编写普通联合的快捷方式:TS Playground


type AttributeType = 'string'|'number'|'custom';

type Attribute<T extends AttributeType|unknown = unknown> = T extends AttributeType ? {
  'string': {type: T, value: string};
  'number': {type: T, value: number};
  'custom': {};
}[T] : {type: unknown, value: unknown};

let attr: Attribute<'string'> = {
  type: 'string',
  value: "1234",
}

let badValue: Attribute<'number'> = {
  type: 'number',
  // Type 'string' is not assignable to type 'number'.
  value: "1234",
}

let badType: Attribute<'string'> = {
  // Type '"number"' is not assignable to type '"string"'.
  type: 'number',
  value: "1234",
}

我还通过添加默认的unknown类型来使Attribute []工作。