考虑中间可选键来推断嵌套值类型 [英] Inferring nested value types with consideration for intermediate optional keys

查看:24
本文介绍了考虑中间可选键来推断嵌套值类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试定义帮助器类型来确定嵌套对象值的类型,同时还考虑任何可选的父键,例如在这样的(或更深的)结构中:

I'm trying to define helper types for determining the type of nested object values, whilst also considering any optional parent keys, e.g. in structures like these (or deeper):

type Foo = { a: { b?: number; } };

type Foo2 = { a?: { b: number } };

就我而言,FooFoo2b 的类型应该被推断为 number |未定义.在 Foo2 中, b 本身不是可选的,但是因为 a 是,对于我的查找目的 b 现在必须是也是可选的……上下文就这么多.

For my purposes, the type of b in both Foo and Foo2 should be inferred as number | undefined. In Foo2 the b is not optional itself, but because a is, for my lookup purposes b must now be optional too... so much for context.

使用这些辅助类型(摘自 更大的集合) 作为构建块:

Using these helper types (extracted from a larger set) as building blocks:

type Keys<T> = keyof Required<T>;

type IsOpt<T> = T extends undefined ? true : never;

type HasOptKey1<T, A> = A extends Keys<T> ? IsOpt<T[A]> : never;

type HasOptKey2<T, A, B> = A extends Keys<T>
    ? IsOpt<T[A]> extends never
        ? HasOptKey1<T[A], B>
        : true
    : never;

type Val1<T, A> = A extends Keys<T> ? T[A] : never;

type Val2<T, A, B> = A extends Keys<T> ? Val1<Required<T>[A], B> : never;

充分利用这些,我们得到:

Putting these to good use, we get:

type F1 = HasOptKey1<Foo, "a">; // never - CORRECT!
type F2 = HasOptKey1<Foo2, "a">; // true - CORRECT!
type F3 = HasOptKey2<Foo, "a", "b">; // true - CORRECT!
type F4 = HasOptKey2<Foo2, "a", "b">; // true - CORRECT!

// infer type of `a` in Foo
type A1 = HasOptKey1<Foo, "a"> extends never
  ? Val1<Foo, "a">
  : Val1<Foo, "a"> | undefined;
// { b: number | undefined; } - CORRECT!

// infer type of `a` in Foo2
type A2 = HasOptKey1<Foo2, "a"> extends never
  ? Val1<Foo2, "a">
  : Val1<Foo2, "a"> | undefined;
// { b: number } | undefined - CORRECT!

// infer type of `b` in Foo
type B1 = HasOptKey2<Foo, "a", "b"> extends never
    ? Val2<Foo, "a", "b">
  : Val2<Foo, "a", "b"> | undefined;
// number | undefined - CORRECT!

// infer type of `b` in Foo2
type B2 = HasOptKey2<Foo2, "a", "b"> extends never
    ? Val2<Foo2, "a", "b">
  : Val2<Foo2, "a", "b"> | undefined;
// number | undefined - CORRECT!

为了避免这些重复的条件,我想使用另一种辅助类型:

To avoid these repeated conditionals, I wanted to use another helper type:

// helper type w/ same logic as used for A1/A2/B1/B2 conditionals
type OptVal<PRED, RES> = PRED extends never ? RES : RES | undefined;

// applied
type OptVal1<T, A> = OptVal<HasOptKey1<T, A>, Val1<T, A>>;

type OptVal2<T, A, B> = OptVal<HasOptKey2<T, A, B>, Val2<T, A, B>>;

然而,即使它似乎适用于 4 种情况中的 3 种情况,A3 被错误地推断为 never,我不明白为什么:

However, even though it seems to be working for 3 out of 4 cases, A3 is incorrectly inferred as never and I don't understand why:

type A3 = OptVal1<Foo, "a">;
// never - WHHHYYY??? (should be same as A1!) <-----

type A4 = OptVal1<Foo2, "a">;
// { b: number } | undefined - CORRECT! (same as A2)

type B3 = OptVal2<Foo, "a", "b">; // number | undefined - CORRECT!

type B4 = OptVal2<Foo2, "a","b">; // number | undefined - CORRECT!

游乐场链接

推荐答案

可能有其他方法可以完成您正在尝试做的事情,但您面临的直接问题是您不小心OptVal 的定义中分发你的条件类型代码>.由于 PRED 是一个类型参数,条件检查 PRED extends never ?RES : RES |undefined 最终会将 PRED 拆分为其联合成员,评估每个成员的条件,然后将结果联合在一起.你的问题是 PREDnever.您可能不会将 never 视为联合类型,但为了一致性起见,编译器将其视为 空联合",输出也将是一个空联合,也就是never.

There might be other ways of accomplishing what you're trying to do, but the immediate problem that you're facing is you are accidentally distributing your conditional type in the definition of OptVal. Since PRED is a type parameter, the conditional check PRED extends never ? RES : RES | undefined will end up splitting PRED into its union members, evaluating the conditional for each member, and unioning back together for the result. And your problem case is when PRED is never. You might not think of never as being a union type, but for consistency's sake the compiler considers it to be the "empty union" and the output will also be an empty union, aka never.

关闭分布式条件类型的最简单方法是采用裸类型参数 PRED 并将其包裹"在一个单元素元组类型中,如下所示:

The easiest way to turn off distributive conditional types is to take the naked type parameter PRED and "clothe" it in a single-element tuple type like this:

type OptVal<PRED, RES> = [PRED] extends [never] ? RES : RES | undefined;

这将使您的案例按预期工作,我认为:

And this will make your cases work as desired, I think:

type A3 = OptVal1<Foo, "a">; // { b?: number | undefined; }

好的,希望有帮助;祝你好运!

Okay, hope that helps; good luck!

游乐场链接到代码

这篇关于考虑中间可选键来推断嵌套值类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆