检查后将可以是A或B的值设置为A的值

时间:2018-09-28 07:59:23

标签: typescript

我有一个值,该值可以是A型或B型,并且显式地是A:

let valueOptional: A|B;
let valueExplicit: A;

现在我进行类型检查,然后设置值:

if ((typeof valueOptional).toString() === 'B') {
  valueExplicit = convertB2A(valueOptional);
} else {
  valueExplicit = valueOptional;
}

现在,编译器仍然会抛出此错误:

Type 'A | B' is not assignable to type 'A'.
  Type 'A' is not assignable to type 'B'.
    Property 'xyz' is missing in type 'B'.

有没有办法解决这个问题?

1 个答案:

答案 0 :(得分:3)

如果您有一个联合,并且想要将联合的类型缩小为它的组成部分之一,则需要使用类型保护。编译器理解为类型保护的内容是有限的,我们可以在给定类型上使用的内容还取决于我们拥有的类型。

对于基元,我们可以使用typeof类型防护:

let valueOptional: number|string;
let valueExplicit: number;

if (typeof valueOptional === 'number') {
  valueExplicit = valueOptional; // valueOptional is number here
} else {
  valueExplicit = +valueOptional //valueOptional is string and we convert
}

对于类,我们可以使用instanceof类型防护:

class A { constructor(public foo: number) { } }
class B { constructor(public bar: number) { } }
let valueOptional: A | B;
let valueExplicit: A;

if (valueOptional instanceof A) {
  valueExplicit = valueOptional; // valueOptional is A here
} else {
  valueExplicit = new A(valueOptional.bar) //valueOptional is B and we convert
}

如果AB是接口,我们可以使用in类型防护根据属性的存在来确定类型(typeof或{{1 }}将起作用,因为在运行时实际上并没有以任何方式表示接口)。 instanceof类型防护可以与类一起使用,但是对于接口,它是唯一的选择:

in

如果条件更复杂,也可以使用自定义类型防护:

interface A { foo: number }
interface B { bar: number }
let valueOptional: A | B;
let valueExplicit: A;

if ('foo' in valueOptional) {
  valueExplicit = valueOptional; // valueOptional is A here
} else {
  valueExplicit = { foo: valueOptional.bar } //valueOptional is B and we convert
}

您还可以混合使用类型保护符:

interface A { foo: number }
interface B { foo: string }
let valueOptional: A | B;
let valueExplicit: A;
// any condition that returns true/false will do, used an arrow function but any function that returns param is type will act as a type guard.
const isA = <T>(v: T | A): v is A => typeof (v as A).foo === 'number'; 

if (isA(valueOptional)) {
  valueExplicit = valueOptional; // valueOptional is A here
} else {
  valueExplicit = { foo: +valueOptional.foo } //valueOptional is B and we convert
}