【问题标题】:Property does not exist on type with union types具有联合类型的类型不存在属性
【发布时间】:2019-08-21 07:50:01
【问题描述】:

我有一个类型试图描述一个可序列化的深层嵌套“纯 javascript”对象:

export interface MappingReferenceValues {
  /** The reference values */
  [key: string]:
    | string
    | number
    | boolean
    | MappingReferenceValues
    | string[]
    | number[]
    | boolean[]
    | MappingReferenceValues[];
}

它让我可以定义数字、字符串、布尔值或它们的数组的简单深层嵌套键值对:

const thing: MappingReferenceValues = {
  test: {
    test2: {
      test3: "hi",
      test4: 200,
      test5: [true, false]
    }
  }
};

太棒了!可以很好地声明它。它比any 好得多,因为它确保我没有函数,而且它与undefined 不同。

但是,使用起来真的很痛苦:

const output = thing.test.test2.test3

出现错误:

Property 'test2' does not exist on type 'string | number | boolean | MappingReferenceValues | string[] | number[] | boolean[] | MappingReferenceValues[]'.
  Property 'test2' does not exist on type 'string'.

test2 不存在于字符串类型上,但它存在于类型 MappingReferenceValues 上。有什么办法可以解决这个问题,而无需再次将其投射到任何地方?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    这个错误对你定义的接口有效。

    如果收到MappingReferenceValues类型的序列化值并反序列化,怎么知道上面有test2嵌套属性?

    使用MappingReferenceValues作为函数的输入参数的例子更符合逻辑:

    function parse(value: MappingReferenceValues) {
        value./* We don't know keys, nor types after the dot */
    }
    

    如果您需要进一步操作刚刚创建的变量thing,则省略类型定义。您仍然可以将其传递给需要 MappingReferenceValues 类型的函数。

    function parse(value: MappingReferenceValues) {
        /* TODO */
    }
    
    const thing = {
        test: {
            test2: {
                test3: "hi",
                test4: 200,
                test5: [true, false]
            }
        }
    };
    
    const output = thing.test.test2.test3; // working
    
    parse(thing); // also working;
    
    

    【讨论】:

    • 这很有趣,因为你说“你怎么知道上面有 test2 嵌套属性?”,但我同样可以问,我怎么知道上面有 test 属性? [key: string] 表示可能有任何字符串属性 - 但可能没有一个名为 test 的属性,甚至根本没有。似乎不一致。同样,它假定它是一个字符串。所以我可以做thing.test.trim(),即使它可能是一个嵌套对象。
    • 您定义了根对象上可以有任何名称的键。它可以是test 或例如random。尝试const output = thing.random.test2.test3 行,您将得到与test 属性完全相同的错误。
    【解决方案2】:
    const thing: MappingReferenceValues = {
      test: {
        test2: {
          test3: "hi",
          test4: 200,
          test5: [true, false]
        }
      },
      newTest: [2]
    };
    

    您需要区分thing.testthing.newTest

    根据MappingReferenceValues 的定义,事物可以包含任何string 键,因此所有这些访问都可以:

    let test = thing.test;
    let newTest = thing.newTest;
    let newTest2 = thing.newTest2; // still 'newTest2' does't not exits on thing.
    

    现在检查testnewTestnewTest2 的类型。它们都可以是string | number | boolean | string[] | MappingReferenceValues | number[] | boolean[] | MappingReferenceValues[]中的任何一个;

    所以要访问它的值,比如test.field,首先你需要将它的类型缩小到MappingReferenceValues

    这种缩小可以用

    来完成
    function isMappingReferneceValues(a: unknown): a is MappingReferenceValues {
      ...
      return true;
    }
    

    【讨论】:

      猜你喜欢
      • 2020-09-27
      • 2023-01-19
      • 2019-10-11
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 2021-06-04
      • 1970-01-01
      • 2018-08-27
      相关资源
      最近更新 更多