- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
鉴于 Typescript: deep keyof of a nested object 中的这段(惊人的)代码
type Cons<H, T> = T extends readonly any[] ?
((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never
: never;
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]]
type Paths<T, D extends number = 10> = [D] extends [never] ? never : T extends object ?
{ [K in keyof T]-?: [K] | (Paths<T[K], Prev[D]> extends infer P ?
P extends [] ? never : Cons<K, P> : never
) }[keyof T]
: [];
这有助于我们将对象的嵌套路径作为元组的联合来获取,如下所示:
type Obj = {
A: { a1: string }
B: { b1: string, b2: { b2a: string } }
}
type ObjPaths = Paths<obj> // ['A'] | ['A', 'a1'] | ['B'] | ['B', 'b1'] | ['B', 'b2'] | ['B', 'b2', 'b2a']
我正在寻找使用路径元组从嵌套属性中检索类型的“反向”方法,格式为:
type TypeAtPath<T extends object, U extends Paths<T>> = ...
问题在于编译器对此签名不满意:类型实例化过深并且可能无限
。
我找到了一种通过缩小 T
来消除此错误的方法:
type TypeAtPath<T extends {[key: string]: any}, U extends Paths<T>> = T[U[0]]
但它只适用于根级别的路径,我担心我的 typescript-foo 不能胜任这项任务。
最佳答案
现在 TypeScript 支持 recursive conditional types和 variadic tuple types ,你可以写DeepIndex
更简单:
type DeepIndex<T, KS extends Keys, Fail = undefined> =
KS extends [infer F, ...infer R] ? F extends keyof T ? R extends Keys ?
DeepIndex<T[F], R, Fail> : Fail : Fail : T;
这可能仍然对树状类型有一些“有趣”的行为,但是自从我在下面写下答案后,情况肯定有所改善:
因此,当我尝试使用与链接问题中相同的不受支持的递归来编写类似的深度索引类型时,我也不断遇到编译器警告或减速。这只是插入编译器做它不应该做的事情的问题之一。也许有一天会出现一种安全、简单且受支持的解决方案,但现在还没有。见 microsoft/TypeScript#26980讨论获得对循环条件类型的支持。
现在我要做的是编写递归条件类型的旧备用:获取预期的递归类型并将其展开为一系列非递归类型,这些类型在一定深度显式退出:
给定 Tail<T>
采用元组类型,如 [1,2,3]
并删除第一个元素以生成更小的元组,如 [2, 3]
:
type Tail<T> = T extends readonly any[] ?
((...t: T) => void) extends ((h: any, ...r: infer R) => void) ? R : never
: never;
我将定义 DeepIndex<T, KS, F>
成为类型 T
的东西和一个键类型元组 KS
然后走到T
使用这些键,生成在那里找到的嵌套属性的类型。如果这最终试图用它没有的键索引某些东西,它将产生一个失败类型 F
, 它应该默认为 undefined
:
type Keys = readonly PropertyKey[];
type DeepIndex<T, KS extends Keys, F = undefined> = Idx0<T, KS, F>;
type Idx0<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx1<T[KS[0]], Tail<KS>, F> : F;
type Idx1<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx2<T[KS[0]], Tail<KS>, F> : F;
type Idx2<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx3<T[KS[0]], Tail<KS>, F> : F;
type Idx3<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx4<T[KS[0]], Tail<KS>, F> : F;
type Idx4<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx5<T[KS[0]], Tail<KS>, F> : F;
type Idx5<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx6<T[KS[0]], Tail<KS>, F> : F;
type Idx6<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx7<T[KS[0]], Tail<KS>, F> : F;
type Idx7<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx8<T[KS[0]], Tail<KS>, F> : F;
type Idx8<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? Idx9<T[KS[0]], Tail<KS>, F> : F;
type Idx9<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? IdxX<T[KS[0]], Tail<KS>, F> : F;
type IdxX<T, KS extends Keys, F> = KS['length'] extends 0 ? T : KS[0] extends keyof T ? T[KS[0]] : F;
在这里你可以看到Idx
type 是几乎递归的,但它不是引用自身,而是引用另一个几乎相同的类型,最终摆脱了 10 层的深度。
我想像这样使用它:
function deepIndex<T, KS extends Keys, K extends PropertyKey>(
obj: T,
...keys: KS & K[]
): DeepIndex<T, KS>;
function deepIndex(obj: any, ...keys: Keys) {
return keys.reduce((o, k) => o?.[k], obj);
}
所以你可以看到 deepIndex()
需要obj
类型 T
和 keys
类型 KS
, 并且应该产生 DeepIndex<T, KS>
类型的结果.实现使用 keys.reduce()
.让我们看看它是否有效:
const obj = {
a: { b: { c: 1 }, d: { e: "" } },
f: { g: { h: { i: true } } }, j: { k: [{ l: "hey" }] }
}
const c = deepIndex(obj, "a", "b", "c"); // number
const e = deepIndex(obj, "a", "d", "e"); // string
const i = deepIndex(obj, "f", "g", "h", "i"); // boolean
const l = deepIndex(obj, "j", "k", 0, "l"); // string
const oops = deepIndex(obj, "a", "b", "c", "d"); // undefined
const hmm = deepIndex(obj, "a", "b", "c", "toFixed"); // (fractionDigits?: number) => string
我觉得不错。
请注意,我确定您会喜欢 deepIndex()
函数或 DeepIndex
实际上键入 约束 KS
输入来自 Paths<T>
的那些而不是输出 undefined
.我尝试了大约五种不同的方法来做到这一点,其中大多数完全破坏了编译器。那些没有让编译器崩溃的比上面的更丑更复杂,而且对于一个踢球者来说,他们真的没有给出有用的错误信息;我不久前提交了一个问题的错误,microsoft/TypeScript#28505 , 导致错误出现在 keys
的错误元素上大批。所以你会想看看
const oops = deepIndex(obj, "a", "b", "c", "d"); // error!
// --------------------------------------> ~~~
// "d" is not assignable to keyof number
但实际上会发生的是
const oops = deepIndex(obj, "a", "b", "c", "d"); // error!
// -----------------------> ~~~
// "d" is not assignable to never
所以我放弃了。如果你敢的话,请随意做更多的工作。整个努力确实将事情推向了一个我不会让任何人其他服从的水平。我认为这是“对编译器来说既有趣又令人兴奋的挑战”,而不是“任何人的生计都应该依赖的代码”。
好的,希望对您有所帮助;祝你好运!
关于typescript - 如何使用 "path tuple"从嵌套属性中检索类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61644053/
我有一个包含文件名 和文件路径 的元组列表。我想找到重复的 filename(但 filepath 可能不同),即文件名相同但 filepath 可能不同的元组。 元组列表示例: file_info
我有一个像这样定义的变量 auto drum = std::make_tuple ( std::make_tuple ( 0.3f , Ex
我有一个包含几个字段的自定义结构,我想在快速 switch 语句中对其进行模式匹配,这样我就可以通过将其中一个字段与另一个字段进行比较来自定义匹配正则表达式。 例如鉴于这种结构: struct MyS
我有一种动态元组结构: template //Should only be tuples class DynamicTuple { vector data; //All data is st
这个问题在这里已经有了答案: What and When to use Tuple? [duplicate] (5 个答案) 关闭 8 年前。 我正在查看 Tuple 的在线示例,但我没有看到任何理
在我的项目中我有很多坐标要处理,在二维情况下我发现(cons x y)的构造比(list x y)快和 (vector x y)。 但是,我不知道如何将 cons 扩展到 3D 或更进一步,因为我没有
我有以下 Scala 代码: def f(x: Int, y: Int): Option[String] = x*y match { case 0 => None case n =>
我的直觉告诉我,在一般情况下,只有宏或复杂类型的体操才能解决这个问题。 Shapeless 或 Scalaz 可以在这里帮助我吗?这是 N=2 问题的具体实例,但我正在寻找的解决方案适用于所有合理的
为什么这段 Scala 代码是这样的: class Test { def foo: (Int, String) = { (123, "123") } def bar: Unit
我是 python 和 pygame 的新手,我正在尝试学习向量和类的基础知识,但在这个过程中我搞砸了,而且我在理解和修复标题中的错误消息方面苦苦挣扎。 这是我的 Vector 类的代码: impor
我正在编写一个程序来打开和读取一个 txt 文件,并在每一行中循环。将第 2 列和第 4 列中的值相乘并将其分配给第 5 列。 A 500.00 A 84.15 ? B 648.80 B 77.61
我知道还有其他几个问题提出了完全相同的问题,但是当我运行时: 导入命令 从 pyDes 导入 * def encrypt(data, password,): k = des(password,
我有一个元组列表,内容如下: >>>myList [(), (), ('',), ('c', 'e'), ('ca', 'ea'), ('d',), ('do',), ('dog', 'ear', '
给定一个 boost::tuple 和 std::tuple,你如何在它们之间进行转换? 也就是说,您将如何实现以下两个功能? template boost::tuple asBoostTuple(
我无法初始化 std::tuple来自 std::tuple 的逐元素元素兼容类型。为什么它不像 boost::tuple 那样工作? #include #include template st
我是 Storm 的新手并且我正在尝试找出如何编写一个 bolt 测试来测试子类 BaseRichBolt 中的 execute(Tuple tuple) 方法。 问题是 Tuple 似乎是不可变的,
如果我有如下元组列表: [('a', 'b'), ('c', 'd'), ('a', 'b'), ('b', 'a')] 我想删除重复的元组(在内容和内部项目顺序方面重复)以便输出为: [('a',
我编写了一个简单的脚本来模拟基于每用户平均收入 (ARPU)、利润率和客户保持客户的年数 (ltvYears) 的客户生命周期值(value) (LTV)。下面是我的脚本。它在“ltvYears =
以下是我的代码,它是一组元组:。输出:设置([(‘A’,20160129,36.44),(‘A’,20160104,41.06),(‘A’,20160201,37.37)])。如何将另一个元组(‘A’
我用以下代码编写了一个程序: import pandas as pd import numpy as np from typing import Tuple def split_data(self,
我是一名优秀的程序员,十分优秀!