简体   繁体   English

如何提取条件是否起作用而不破坏 TypeScript 中的缩小类型

[英]How to extract if condition to function and not break narrowed types in TypeScript

given the following if statement:给出以下 if 语句:

interface MyData {
    prop1?: number,
    prop2?: number,
}

const x: MyData = {
    prop1: 1,
    prop2: 2,
}

function fun(a: number, b: number) {
    console.log(a, b);
}

if (x.prop1 && x.prop2) {
  fun(x.prop1, x.prop2);
}

the compiler is able to infer that inside the if body, x.prop1 is defined and x.prop2 is defined.编译器能够推断在 if 主体内部,定义了 x.prop1 并定义了 x.prop2。

I want to give name to the condition by extracting it to a function.我想通过将其提取到函数来命名条件。

function bothPropsDefined(prop1: number | undefined, prop2: number | undefined): boolean {
    return !!prop1 && !!prop2;
}

if (bothPropsDefined(x.prop1, x.prop2)) {
  fun(x.prop1, x.prop2);
}

but the compiler fails to conclude that in the if body, both props are defined.但编译器未能得出结论,在 if 主体中,两个 props 都已定义。

// Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
   Type 'undefined' is not assignable to type 'number'.(2345)

How can I give the compiler a hint that if bothPropsDefined returns true, than prop1 and prop2 are defined?我怎样才能给编译器一个提示,如果bothPropsDefined返回 true,那么 prop1 和 prop2 被定义了?

I tried type guard:我试过类型保护:

function bothPropsDefinedGuard(prop1: number | undefined, prop2: number | undefined): prop1 is number {
    return !!prop1 && !!prop2;
}

if (bothPropsDefinedGuard(x.prop1, x.prop2)) {
  fun(x.prop1, x.prop2);
}

but I only managed to add guard to one property.但我只设法为一个属性添加了警卫。

Update更新

for the question above, I managed to write:对于上述问题,我设法写了:

function bothPropsDefinedGuard2(x: {prop1?: number, prop2?: number}): x is {prop1: number, prop2: number} {
    return !!x.prop1 && !!x.prop2;
}

if (bothPropsDefinedGuard2(x) {
  fun(x.prop1, x.prop2);
}

but my real case is a bit harder, as props come from multiple objects:但我的真实情况有点困难,因为道具来自多个对象:

interface MyData1 {
    prop1?: number,
}

interface MyData2 {
    prop2?: number,
}

const x1: MyData1 = {
    prop1: 1,
}

const x2: MyData2 = {
    prop2: 2,
}

You are looking for custom type guards.您正在寻找自定义类型的守卫。 Here is a solution:这是一个解决方案:

const x: { prop1: number | undefined, prop2: number | undefined } = {
  prop1: 1,
  prop2: 2,
}

function fun(a: number, b: number) {
  console.log(a, b);
}

// Custom type guard
function propDefined(prop: number | undefined): prop is number {
  return typeof prop === 'number';
}

// Use
if (propDefined(x.prop1) && propDefined(x.prop2)) {
  fun(x.prop1, x.prop2);
}

Note: You cannot have a type guard that changes the types for two arguments.注意:你不能有一个类型保护来改变两个参数的类型。 A type guard can only narrow one argument eg prop is number as shown.类型保护只能缩小一个参数,例如prop is number ,如图所示。

My goal was to name the condition, so answer by @basarat is not ideal.我的目标是命名条件,所以@basarat 的回答并不理想。

I ended up我结束了

  • creating an artificial combined object out of individual data pieces从单个数据片段中创建人工组合对象
  • making a type guard on the combined object在组合对象上制作类型保护
  • using combined object in the if body在 if 主体中使用组合对象

This is not ideal as well, due to artificially added combined object, but lets me name the condition:由于人为添加的组合对象,这也不理想,但让我命名条件:

interface MyData1 {
    prop1?: number,
}

interface MyData2 {
    prop2?: number,
}

const x1: MyData1 = {
    prop1: 1,
}

const x2: MyData2 = {
    prop2: 2,
}

const xCombined: MyData = {
    prop1: x1.prop1,
    prop2: x2.prop2,
}

if (bothPropsDefinedGuard2(xCombined)) {
  fun(xCombined.prop1, xCombined.prop2);
}

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

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