typescript - 在考虑中间可选键的情况下推断嵌套值类型

标签 typescript type-inference mapped-types conditional-types

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

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

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

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

使用这些辅助类型(从 larger set 中提取)作为构建 block :

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;

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

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!

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

// 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,我不明白为什么:

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!

Playground link

最佳答案

可能有其他方法可以完成您想要做的事情,但您面临的直接问题是您不小心 distributing your conditional typeOptVal 的定义中。由于 PRED 是一个类型参数,条件检查 PRED extends never ?资源:资源 | undefined 最终会将 PRED 拆分成它的联合成员,评估每个成员的条件,然后联合起来得到结果。你的问题是 PREDnever。您可能不会将 never 视为联合类型,但为了保持一致性,编译器将其视为 the "empty union"。并且输出也将是一个空联合,又名 never

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

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

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

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

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

Playground link to code

关于typescript - 在考虑中间可选键的情况下推断嵌套值类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60869412/

相关文章:

java - 使用 ionic 3 进行 HTTP Post

javascript - Angular 中的 Json 数据到 CSS

typescript - 如何从对象元组创建对象类型?

typescript - 具有映射类型的代理

javascript - SVGElement 类型不存在属性 getBBox

reactjs - 如何在 Amplify 数据存储中创建动态查询

c# - 为什么编译器不推断泛型类型

haskell - 为什么 3 和 x(被分配为 3)在 Haskell 中具有不同的推断类型?

haskell - Haskell 如何执行 Beta 转换来派生类型?

typescript - 空对象的索引签名和记录之间的区别?