获取字典/对象键作为打字稿中的元组

2023-12-12

我想从 TS 3.1 中的对象中获取具有正确类型文字的正确元组类型:

interface Person {
  name: string,
  age: number
}

// $ExpectType ['name','age']
type ObjectKeysTuple = ToTuple<keyof Person>

Why?:

使用时获取正确的字符串文字元组Object.keys(dictionary)

我无法找到解决方案,因为keyof扩大到联合并返回('name' | 'age')[]这绝对不是我们想要的。

type ObjectKeysTuple<T extends object> = [...Array<keyof T>]
type Test = ObjectKeysTuple<Person>
// no errors ????not good
const test: Test = ['age','name','name','name']

Related:

  • 将接口转换为打字稿中的元组
  • 将并集类型转换为交集类型

您提到的用例,提出了一个元组类型Object.keys(),充满危险,我建议不要使用它。

第一个问题是 TypeScript 中的类型不是"exact"。也就是说,只是因为我有一个 type 的值Person,这并不意味着该值包含only the name and age特性。想象一下以下情况:

interface Superhero extends Person {
   superpowers: string[]
}
const implausibleMan: Superhero = { 
   name: "Implausible Man",
   age: 35,
   superpowers: ["invincibility", "shape shifting", "knows where your keys are"]
}
declare const randomPerson: Person;
const people: Person[] = [implausibleMan, randomPerson];
Object.keys(people[0]); // what's this?
Object.keys(people[1]); // what's this?

注意如何implausibleMan is a Person额外superpowers财产,以及randomPerson is a Person谁知道有什么额外的属性。你根本不能说Object.keys()作用于Person将产生一个数组only已知的键。这就是主要原因此类功能请求继续得到rejected.

第二个问题与键的顺序有关。即使您知道您正在处理包含所有内容的确切类型and only从接口声明的属性,你不能保证密钥将由以下方式返回Object.keys()与接口的顺序相同。例如:

const personOne: Person = { name: "Nadia", age: 35 };
const personTwo: Person = { age: 53, name: "Aidan" };
Object.keys(personOne); // what's this?
Object.keys(personTwo); // what's this?

最合理的 JS 引擎将probably按照插入的顺序将属性交还给您,但您不能指望这一点。而且您当然不能指望插入顺序与 TypeScript 接口属性顺序相同。所以你很可能会治疗["age", "name"]作为类型的对象["name", "age"],这可能不太好。


综上所述,我喜欢搞乱类型系统,所以我决定编写代码,以类似于以下的方式将联合转换为元组马特·麦卡琴对另一个问题的回答。它也充满危险,我建议不要这样做。以下注意事项。这里是:

// add an element to the end of a tuple
type Push<L extends any[], T> =
  ((r: any, ...x: L) => void) extends ((...x: infer L2) => void) ?
  { [K in keyof L2]-?: K extends keyof L ? L[K] : T } : never

// convert a union to an intersection: X | Y | Z ==> X & Y & Z
type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never

// convert a union to an overloaded function X | Y ==> ((x: X)=>void) & ((y:Y)=>void)     
type UnionToOvlds<U> = UnionToIntersection<U extends any ? (f: U) => void : never>;

// convert a union to a tuple X | Y => [X, Y]
// a union of too many elements will become an array instead
type UnionToTuple<U> = UTT0<U> extends infer T ? T extends any[] ?
  Exclude<U, T[number]> extends never ? T : U[] : never : never

// each type function below pulls the last element off the union and 
// pushes it onto the list it builds
type UTT0<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT1<Exclude<U, A>>, A> : []
type UTT1<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT2<Exclude<U, A>>, A> : []
type UTT2<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT3<Exclude<U, A>>, A> : []
type UTT3<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT4<Exclude<U, A>>, A> : []
type UTT4<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTT5<Exclude<U, A>>, A> : []
type UTT5<U> = UnionToOvlds<U> extends ((a: infer A) => void) ? Push<UTTX<Exclude<U, A>>, A> : []
type UTTX<U> = []; // bail out

让我们尝试一下:

type Test = UnionToTuple<keyof Person>;  // ["name", "age"]

看起来有效。

注意事项:您不能以编程方式对任意大小的联合执行此操作。 TypeScript 不允许你迭代联合类型,因此这里的任何解决方案都将选择一些最大联合大小(例如,六个组成部分)并处理达到该大小的联合。我上面有些迂回的代码是为了让您可以通过复制和粘贴来扩展这个最大大小。

另一个警告:它取决于编译器能够按顺序分析条件类型中的重载函数签名,并且取决于编译器能够在保留顺序的同时将联合转换为重载函数。这些行为都不一定保证以相同的方式继续工作,因此每次新版本的 TypeScript 出现时,您都需要检查这一点。

最后的警告:它还没有经过太多测试,所以即使你保持 TypeScript 版本不变,它也可能充满各种有趣的陷阱。如果您真的想使用这样的代码,那么在考虑在生产代码中使用它之前,您需要对其进行大量测试。


总之,不要做我在这里展示的任何事情。好的,希望有帮助。祝你好运!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

获取字典/对象键作为打字稿中的元组 的相关文章

随机推荐

  • 尝试更改 Google 云端硬盘中文件的所有者

    我尝试更改云端硬盘中文档的所有权 但收到以下错误 很抱歉 服务器发生错误 请稍等一下 然后重试 第 12 行 文件 代码 function transferFiles var files DriveApp getFiles while fi
  • JNA 与 Fortran 假定大小的数组

    我有一个 Fortran 子例程 采用假定大小的数组 subroutine sub arr implicit none double precision arr end subroutine 我使用 JNA 从 Java 进行了本机调用 F
  • Delphi:发生意外的内存泄漏

    在 Delphi 中 我已配置为报告内存泄漏 IFDEF Debug ReportMemoryLeaksOnShutdown true ENDIF After exiting the program I get the following
  • 在bash shell脚本中初始化动态变量(可变变量)

    我通过 bash shell 使用 PHP CLI 请检查在 shell 脚本中操作数组 由 php cli 打印 了解详情 在下面的 shell 代码中我能够回显key value我从 PHP 脚本中获得的对 IFS parse php
  • wp7水平滑动选择

    我正在寻找一个允许我滑动项目列表的控件 水平滑动将在下一个和上一个项目之间移动 该控件还将确保所选项目在不被操作时移动到中心 该控件仅占据页面的一半 我希望左侧和右侧的选项可见并环绕 Like so lt gt 所以我的问题是 这样的控件是
  • Google Play - 零支持的设备

    我知道这里有类似的问题 但似乎没有一个令人满意的答案 我正在尝试发布应用程序 但无论我尝试什么 开发人员控制台都会报告支持的设备数为零 这是我的完整清单
  • 如何获取R脚本出错时的行号?

    如果我从命令行运行一个很长的 R 脚本 R slave script R 那么我怎样才能让它在错误时给出行号呢 如果可能的话 我不想将调试命令添加到脚本中 我只是希望 R 表现得像大多数其他脚本语言一样 这不会给您行号 但它会告诉您调用堆栈
  • WPF 与 Windows 窗体

    我对 WPF 和 Windows 窗体非常困惑 WPF 相对于 Windows 窗体的用途是什么 WPF有什么用 WPF 是一个用于开发 Windows 和浏览器 应用程序的新平台 WPF不一定有replaceWindows 窗体 使用 W
  • Dojo 中的 DataGrid,包含来自 servlet 的 json 数据

    我第一次使用 JSON 并想用我的 JSON 数据填充我的数据网格 这是我的 JSON 数据 head vars s fname lname results bindings s type uri value http tn gov in
  • 按键对散列进行分组并对值求和

    我有一个哈希数组 Vegetable gt 10 Vegetable gt 5 Dry Goods gt 3 gt Dry Goods gt 2 我需要使用inject我想 但我真的一直在挣扎 我想要一个新的哈希值来反映前一个哈希值的重复键
  • 如何使用 PHP 解码以“\u”开头的内容

    如何使用 PHP 解码以 u 开头的内容 e g u4f60 u5df2 u7ecf u6dfb u52a0 u4e86 u6b64 u8bdd u9898 谢谢 对于 PHP 5 4 intl s u4f60 u5df2 u7ecf u6
  • 如何使用 PHP DOMDocument::saveHTML() 阻止 html 实体?

    由于自定义存储需求 为什么 在这里并不重要 谢谢 我必须保存 html a 特定格式的链接 例如 myDOMNode gt setAttribute href 123456 一切正常 直到我打电话saveHTML 在包含的 DOMDocum
  • AJAX POST 到 PHP(无需 JQuery)

    我有一个 PHP 作业 我决定尝试添加 AJAX 因为在我们的课堂上我们不会只学习 AJAX 而只会学习 PHP 我似乎无法得到工作的回应 然而 在 Fire Fox 控制台的网络部分中 我可以找到使用我在表单中输入的值发送的 POST 以
  • 多标签分类的特征选择 (scikit-learn)

    我正在尝试在 scikit learn 中通过卡方方法进行特征选择 sklearn feature selection SelectKBest 当我尝试将此应用于多标签问题时 我收到此警告 UserWarning Duplicate sco
  • 如何使用slf4j框架实现敏感数据的屏蔽?

    我想使用 slf4j 框架屏蔽敏感数据 例如用户名 密码 感谢您立即提供帮助 提前致谢 试试这个 1 首先 我们应该创建一个类来处理我们的日志 每行 public class PatternMaskingLayout extends Pat
  • 如何通过 JavaScript 将条目插入浏览历史记录

    如何在浏览历史记录中插入条目 以便后退按钮第一次单击时转到不同页面 第二次单击时转到原始页面 因此 如果您需要对我想要做什么进行详细解释 请访问 https secure exitjunction com howitworks jsp 我只
  • 在 Fortran 中存储具有多维索引的变量

    Question 考虑以下代码 program example implicit none integer parameter n coeffs 1000 integer parameter n indices 5 integer i re
  • c++ 执行时间比 python 慢

    我改用c 因为我听说它比python快400倍 但是当我制作一个无限循环来递增变量并打印其值时 python似乎更快 怎么可能呢 以及如何优化呢 Python脚本 x 1 while 1 print x x 1 C 代码 int x 1 w
  • 迭代器与 for 循环以及为什么像 for 循环一样引入迭代器? [复制]

    这个问题在这里已经有答案了 可能的重复 Java中增强的for循环和迭代器有什么优点 for 循环和 for each 循环之间有性能差异吗 下面的代码显示 使用 for 循环和迭代器 我们都可以迭代集合的元素 那么 for 循环和迭代器之
  • 获取字典/对象键作为打字稿中的元组

    我想从 TS 3 1 中的对象中获取具有正确类型文字的正确元组类型 interface Person name string age number ExpectType name age type ObjectKeysTuple ToTup