【问题标题】:Working with a discriminated Union-type instead of optional properties使用可区分的联合类型而不是可选属性
【发布时间】:2020-01-25 07:03:06
【问题描述】:

我目前正在将 JavaScript 代码库重写为 Typescript。我发现函数中的数据定义如下 MyProps:

interface MyProps {
  type: string;
  foo?: unknown;
  bar?: unknown;
  someCommonProps: unknown;
}

用法一直是这样的:

const myFunction = (props: MyProps) => {
  const { type, foo, bar, someCommonProps } = props;
  if (foo) {
    //Do something
  }
  if (bar) {
    //Do something else
  }
};

通过进一步调查,我发现接口/类型可以像这样更精确地定义:

interface Foo {
  type: "a" | "b" | "c";
  foo: unknown;
}

interface Bar {
  type: "d" | "e" | "f";
  bar: unknown;
}

type FooBar = Foo|Bar;

type MyProps = FooBar & {
  someCommonProps: unknown;
}

但是通过将新的 MyProps 分配给 props,我会在 myFunction 的第一行得到一个错误:Property 'foo' does not exist on type 'MyProps'。与“酒吧”类似。在不以可能引入错误的方式重写的情况下处理此问题的最佳方法是什么?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    虽然不那么优雅,但我认为这是实现这一目标的最简单方法:

    const myFunction = (props: MyProps) => {
      const { type, someCommonProps } = props;
      const foo = 'foo' in props ? props.foo : undefined;
      const bar = 'bar' in props ? props.bar : undefined;
      if (foo) {
        //Do something
      }
      if (bar) {
        //Do something else
      }
    };
    

    还要考虑是否可以这样做:

    const foobar = 'foo' in props ? props.foo : props.bar;
    

    【讨论】:

      【解决方案2】:

      有了类,你可以这样做:

      class Foo {
          type: 'a' | 'b' | 'c';
          foo: unknown;
      }
      
      class Bar {
          type: 'd' | 'e' | 'f';
          bar: unknown;
      }
      
      type FooBar = Foo | Bar;
      
      type MyProps = FooBar & {
          someCommonProps: unknown;
      };
      
      const myFunction = (props: MyProps) => {
          if (props instanceof Foo) {
              // Do something
              console.log(props.someCommonProps);
          }
          if (props instanceof Bar) {
              // Do something else
          }
      };
      

      【讨论】:

        【解决方案3】:

        如果您的标签是字符串联合,您可以使用switch(props.type) 并枚举案例中的所有值:

        const myFunction = (props: MyProps) => {
          switch (props.type) {
            case 'a': case 'b': case 'c':
              let x = props.foo;
              break;
            case 'd': case 'e': case 'f':
              let y = props.bar;
              break;
          }
        };
        

        从长远来看,这可能会很乏味,这是一种可能的自动化方法:

        let FooTag = ['a', 'b', 'c'] as const;
        
        interface Foo2 {
          type: typeof FooTag[number];
          foo: unknown;
        }
        
        function isFoo2(x: Foo2 | Bar2): x is Foo2 {
          return (FooTag as readonly string[]).indexOf(x.type) >= 0;
        }
        ...
        
        const myFunction2 = (props: MyProps2) => {
          if (isFoo2(props)) {
            let x = props.foo;
          }
          if (isBar2(props)) {
            let x = props.bar;
          }
        

        Play

        【讨论】:

          猜你喜欢
          • 2019-05-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-05-31
          • 1970-01-01
          • 2020-05-01
          • 2020-02-16
          • 1970-01-01
          相关资源
          最近更新 更多