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

时间:2019-09-25 06:36:38

标签: typescript

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

.v-calendar-daily_head-day-label {
    display: none;
}

用法一直是这样的:

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

但是通过将新的MyProps分配给props,我将在myFunction的第一行得到一个错误:属性'foo'在类型'MyProps'上不存在。与“酒吧”类似。不用重写可能会引入错误的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

对于类,您可以这样操作:

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

答案 1 :(得分:1)

如果您的标签是字符串联合,则可以使用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

答案 2 :(得分:0)

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

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

还要考虑是否可以执行以下操作:

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