【问题标题】:Typescript: type determination based on property valueTypescript:基于属性值的类型确定
【发布时间】:2018-07-06 04:55:04
【问题描述】:

我很确定 Typescript 能够根据属性值确定扩展类,例如:

interface Base {
    type: string;
    child?: Base;
}

interface Ext extends Base {
    type: 'test';
    sth: string;
}

z({
    type: 'a',
    child: {
        type: 'b',
    }
}); // ok

z({
    type: 'a',
    child: {
        type: 'test',
        sth: 'val'
    }
}); // not ok

function z(input: Base) { }

上面的例子不起作用,TS 告诉我属性sth 在接口Base 上不存在。由于type 属性上的值'test',我需要更改什么以便TS 理解孩子实际上是Ext 的类型?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    你需要将它声明为Ext类型并且它应该通过

    let x: Ext = {
        type: 'test',
        sth: 'value'
    }
    

    【讨论】:

    • 不应该 TS 能够确定它一定是Ext,因为Ext 扩展了Base,但是根据type 的值做出了明确的区分?
    • Ext 的类型仍然是 Base(这样可以工作),但它包含基本接口中不存在的键。
    【解决方案2】:

    我想我明白了:

    interface Base {
        child?: Ext;
    }
    
    interface Ext1 extends Base {
        type: 'a';
    }
    interface Ext2 extends Base {
        type: 'test';
        sth: string;
    }
    
    type Ext = Ext1 | Ext2;
    
    z({
        type: 'a',
        child: {
            type: 'test',
            sth: 'x'
        }
    });
    
    function z(input: Ext) { }
    

    如果type'test' 而没有定义sth 而不是相反,则此示例将失败

    【讨论】:

      【解决方案3】:

      此错误来自excess property check,仅在使用对象字面量初始化变量时才会发生。为了避免这种情况,您需要使用不是对象文字的东西来初始化值,例如您可以添加中间变量

      let o = {
          type: 'test',
          sth: 'value'
      }
      let x1: Base = o;
      

      或者你可以添加一个类型断言

      let x2: Base = {
          type: 'test',
          sth: 'value'
      } as Base;
      

      另一个解决方案是使Basez 泛型化,参数化为Child 的类型,它应该是Base 的子类型(请注意,自引用类型约束很难正确处理,但它似乎在这种情况下有效,小问题是Base 的默认值导致约束中的Base 被推断为Child extends Base<{ type: string; child?: Base<any> | undefined; } - any 这里可能有问题,但似乎不会影响示例)。

      interface Base<Child extends Base = { type: string, child?: Base }> {
          type: string;
          child?: Child;
      }
      
      interface Ext extends Base {
          type: 'test';
          sth: string;
      }
      
      z({
          type: 'a',
          child: {
              type: 'b',
          }
      }); // ok
      
      z({
          type: 'a',
          child: {
              type: 'test',
              sth: 'val'
          }
      }); // not ok
      
      function z<B extends Base>(input: B) { }
      

      【讨论】:

      • 两者都可以,但我很确定我曾经看到过一个例子,说明 TS 如何自己解决这个问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-14
      • 2021-10-07
      • 1970-01-01
      • 2020-06-16
      • 2021-03-26
      • 2019-07-23
      • 2023-04-07
      相关资源
      最近更新 更多