我正在尝试定义辅助类型以确定嵌套对象值的类型,同时还考虑任何可选的父键,例如在像这样(或更深层次)的结构中:
type Foo = { a: { b?: number; } };
type Foo2 = { a?: { b: number } };
就我而言,Foo
和Foo2
中b
的类型应该被推断为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!
最佳答案
可能有其他方法可以完成您想要做的事情,但您面临的直接问题是您不小心 distributing your conditional type在 OptVal
的定义中。由于 PRED
是一个类型参数,条件检查 PRED extends never ?资源:资源 | undefined
最终会将 PRED
拆分成它的联合成员,评估每个成员的条件,然后联合起来得到结果。你的问题是 PRED
是 never
。您可能不会将 never
视为联合类型,但为了保持一致性,编译器将其视为 the "empty union"。并且输出也将是一个空联合,又名 never
。
关闭分布式条件类型的最简单方法是采用裸类型参数 PRED
并将其“包裹”在单元素元组类型中,如下所示:
type OptVal<PRED, RES> = [PRED] extends [never] ? RES : RES | undefined;
我认为这将使您的案例按预期工作:
type A3 = OptVal1<Foo, "a">; // { b?: number | undefined; }
好的,希望对你有帮助;祝你好运!
关于typescript - 在考虑中间可选键的情况下推断嵌套值类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60869412/