访问联合类型属性的惯用方式

Idiomatic way to access properties of union type

访问联合类型中合并的类型之一可能缺少的属性的惯用方法是什么?

type DataColumn = {
  value: number;
};

type CalculatedColumn = {
  calculation: string;
};

type Column = DataColumn | CalculatedColumn;

function getValue(c: Column) {
  return c.value || c.calculation;
}

typecheck 导致以下错误:

13:   return c.value || c.calculation;
                          ^ property `calculation`. Property not found in
13:   return c.value || c.calculation;
                        ^ object type

@dfkaye 在 twitter 上指出,如果 "default" 案例抛出错误,它会起作用:

function e() {
  throw new Error('foo');
}

function getValue(c: Column) {
  return c.value || c.calculation || e();
}

谁能解释一下:

  1. 为什么有效?是故意的还是副作用?
  2. 为什么有必要?列类型总是 valuecalculation,因此永远不会发生错误情况。
  3. 是否有更好、更惯用的方法?
  4. 这是一种安全的方法吗,还是将来可能会失效?

PS:似乎在 TypeScript it can be done 中使用类型断言。

惯用的方法是使用 disjoint unions。这通过没有错误:

type DataColumn = {
  kind: 'data';
  value: number;
};

type CalculatedColumn = {
  kind: 'calculated';
  calculation: string;
};

type Column = DataColumn | CalculatedColumn;

function e() {
  throw new Error('foo');
}

function getValue(c: Column) {
  return c.kind === 'data' ? c.value : c.calculation;
}

getValue({kind: 'data', value: 123});
getValue({kind: 'calculated', calculation: 'foo'});

我不确定为什么你描述的情况不起作用。我想不出任何不健全的理由。但是不相交的工会确实有效。

Why it works? Is it intentional, or a side effect?

这很可能是一个错误,Flow simple 忽略所有分支,但最后:

function getValue(c: Column) {
  return c.value || c.calculation || undefined;
}

Why is it necessary? Column type has always either value or calculation, so error case should never happen

这就是你错的地方。如果值具有类型 { value: number },则意味着它可以具有任何类型的任何其他 属性,包括类型 stringcalculation 或可能是其他类型。

Is there a better, more idiomatic way?

是的,请参阅 Nat Mote 的回答

Is this a safe approach, or is it likely to break in future?

原则上不安全,以后很可能坏掉

Seems like in TypeScript it can be done using type assertions.

您可以在 Flow 中做同样的事情,但它不安全:

function getValue(c: Column) {
  return ((c: any): DataColumn).value || ((c: any): CalculatedColumn).calculation;
}

另外你不要忘记数字和字符串可以是假的。