typescript 类型脚本:在嵌套类型narrow之后获取类型

hpcdzsge  于 2023-02-13  发布在  TypeScript
关注(0)|答案(1)|浏览(140)

我想提取一个narrow类型的结果类型,我有一个Parent类型,它有一个x属性,而x属性又有两个ab属性,其中至少有一个必须被设置,我用联合体对此建模:

type A = {
    type: "A",
    a: string
}

type B = {
    type: "B",
    b: string
}

type AB = {
    type: "AB",
    a: string
    b: string
}

type Parent = {
    x: A | B | AB
    // potentially many more properties
}

const ps : Parent[] = []

使用类型鉴别器可以很好地进行类型收缩。

ps.map(p => {
  if(p.x.type == "AB") {
    p.x.a // works!
  }
})

在我的代码的某些地方,我只处理同时具有ab属性的父对象,例如,通过使用过滤器。

const asAndBs = ps.filter((p) : p is AAndBParent => p.x.type == "AB")

为此,我需要有一个显式引用到缩小类型AAndBParent,它将被定义为类型Parent,其中Parent.x.type == "AB".我能想到的唯一方法是"从头开始"创建一个新类型.这是繁琐的,如果父有很多属性,这将不得不在类型定义之间同步.

type AAndBParent = {
    x: AB
    // would have to copy all other properties from Parent
}
    • 问题1**:可以相对于父对象定义AAndBPanret吗?
    • 问题2**:我可以用类型安全的方式定义过滤函数吗?

我可能写错了类型 predicate ,但编译器不会抱怨,编译器的类型收缩逻辑知道得更清楚。

const asAndBs = ps.filter((p) : p is AAndBParent => p.x.type != "AB")
asAndBs[0].x.a // runtime error: potentially undefined
yc0p9oo0

yc0p9oo01#

我可以定义与Parent相关的AAndBParent吗?
是-要从现有类型派生新类型并同时缩小一个或多个属性,可以使用与具有缩小属性的类型的交集,如下所示:
TSPlayground

type Parent = {
  x: A | B | AB;
  // Potentially many more properties, for example:
  isParent: true;
};

type AAndBParent = Parent & { x: AB };

declare const ab: AAndBParent;

ab.x
 //^? (property) x: AB

ab.isParent
 //^? (property) isParent: true

我可以用类型安全的方式定义过滤函数吗?
好耶!
然而,创建类型保护函数可能需要一些技巧--它们的存在是为了让您在内置控制流分析实际上无法提供您所期望的收缩的地方帮助编译器--它们在概念上与类型Assert相关,因为它们是让您提供比编译器更多信息的机制。
TypeScript可以防止你犯这样的明显错误:

declare const ps: Parent[];

ps.filter((p): p is AAndBParent => p.x.oops === "AB"); /* Error
                                       ~~~~
Property 'oops' does not exist on type 'A | B | AB'.
  Property 'oops' does not exist on type 'A'.(2339) */

但是您要负责确保在函数体中执行的运行时验证与 predicate 中的类型一致。
在您的例子中,您只是验证对象的形状是否为{ x: { type: "AB" } },因此只需在 predicate 中使用它,并让编译器推断其余部分-它将按预期工作:

const asAndBs = ps.filter(
  (p): p is typeof p & { x: { type: "AB" } } => p.x.type === "AB"
);

const [first] = asAndBs;

if (first) {
  first.isParent
      //^? (property) isParent: true

  first.x.type
        //^? (property) type: "AB"

  first.x.a
        //^? (property) a: string

  first.x.b
        //^? (property) b: string
}

TSPlayground代码

相关问题