简体   繁体   English

使用可区分的联合类型而不是可选属性

[英]Working with a discriminated Union-type instead of optional properties

I'm currently in the process of rewriting a JavaScript codebase into Typescript.我目前正在将 JavaScript 代码库重写为 Typescript。 I've discovered that the data in a function is defined like MyProps below:我发现 function 中的数据定义如下 MyProps:

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

The usage has been like this:用法是这样的:

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

By further investigation I see that the interfaces/types could be defined more precisely like this:通过进一步调查,我发现接口/类型可以像这样更精确地定义:

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;
}

But by assigning the new MyProps to props, I will get an error on the first line of myFunction: Property 'foo' does not exist on type 'MyProps'.但是通过将新的 MyProps 分配给 props,我会在 myFunction 的第一行得到一个错误:Property 'foo' does not exist on type 'MyProps'。 And similar with 'bar'.与“酒吧”类似。 What is the best way to handle this without rewriting in a way that may introduce bugs?在不以可能引入错误的方式重写的情况下处理此问题的最佳方法是什么?

With classes you can do this this way:使用类,您可以这样做:

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
    }
};

If your tags are string unions, you can use switch(props.type) and enumerate all values in the cases:如果您的标签是字符串联合,您可以使用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;
  }
};

This can be tedious in the long run, here's one possible way to automate:从长远来看,这可能很乏味,这是一种可能的自动化方法:

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

While not as elegant, I think this is the easiest way achieve this:虽然不那么优雅,但我认为这是实现这一目标的最简单方法:

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
  }
};

Also consider if you can do something like this:还要考虑是否可以执行以下操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM