Typescript 键值关系保留 Object.entries 类型

Mal*_*ter 18 typescript typescript-generics

用于打字Object.entries的打字稿提供具有返回类型[string, T][],但是我正在寻找一个泛型类型Entries<O>来表示这个功能,保持键和值之间的关系的返回值。

例如。当有一个对象类型时

type Obj = {
    a: number,
    b: string,
    c: number
}
Run Code Online (Sandbox Code Playgroud)

我正在寻找一种类型Entries<O>,当提供时会产生以下类型之一(或类似的东西)Obj

(["a", number] | ["b", string] | ["c", number])[]
[["a", number], ["b", string], ["c", number]]
(["a" | "c", number] | ["b", string])[]
Run Code Online (Sandbox Code Playgroud)

这对于 Object.entries 的所有用例(请参阅此处)并不正确,这对我的特定情况来说是没有问题的。


尝试和失败的解决方案:

type Entries<O> = [keyof O, O[keyof O]][]不适合这个工作,因为它不仅保留了可能键和值而不是这些作为之间的关系Entries<Obj>["a" | "b" | "c", number | string]

type Entry<O, K extends keyof O> = [K, O[K]]
type Entries<O> = Entry<O, keyof O>[]
Run Code Online (Sandbox Code Playgroud)

这里的定义Entry按预期工作,例如。Entry<Obj, "a">["a", number]但它在第二行中的应用程序具有keyof O相同的结果作为第一个尝试为第二类型可变引线一次。

kay*_*ya3 24

这是一个解决方案,但在使用它作为;的返回类型时要小心Object.entries。这样做并不总是安全的(见下文)。


当您想将每个键与依赖于该键类型的东西配对时,请使用映射类型

type Entries<T> = {
    [K in keyof T]: [K, T[K]];
}[keyof T][];

type Test = Entries<Obj>;
// (["a", number] | ["b", string] | ["c", number])[]
Run Code Online (Sandbox Code Playgroud)

第二个版本具有包含属性而不是联合的元组类型,构造起来要困难得多;可以将联合转换为元组,但您基本上不应该这样做。

第三个版本易于管理,但比第一个版本复杂一些:您需要PickByValue这个答案中获取

type Entries3<T> = {
    [K in keyof T]: [keyof PickByValue<T, T[K]>, T[K]]
}[keyof T][];

type Test3 = Entries3<Obj>;
// (["a" | "c", number] | ["b", string])[]
Run Code Online (Sandbox Code Playgroud)

游乐场链接


我想我还应该解释为什么 Typescript没有Object.entries. 当你有一个像type Obj = {a: number, b: string, c: number}这样的类型时,它只能保证一个值具有这些属性;它保证该值不也有其他属性。例如,该值{a: 1, b: 'foo', c: 2, d: false}可分配给类型Obj(不考虑对象文字的额外属性检查)。

在这种情况下,Object.entries将返回一个包含元素的数组['d', false]。类型Entries<Obj>说这不可能发生,但实际上它可能发生;soEntries<T>不是一般的声音返回类型Object.entries。只有Object.entries当您自己知道这些值不会有多余的属性时,才应该使用上述解决方案;Typescript 不会为你检查这个。

  • 虽然这可能是一个真正的动机,但它们很容易将返回类型设置为“Entries&lt;T&gt; &amp; [string, T][]”。它可能不是有效的 TS,因为我在类型级别上不太擅长,但他们可以保留类型 T 的想法*至少*具有这些属性,可能还有更多属性(例如示例中的“d”)。然后绝对有可能从这些值构造一个对象并保证它仍然处于类型 T 的有效形状 (3认同)

Pet*_*nas 23

目前,已经引入了一个名为type-fest的非常好的实用程序库,以Entries. 你可以像这样使用它:

import { Entries } from 'type-fest';

Object.entries(obj) as Entries<typeof obj>;
Run Code Online (Sandbox Code Playgroud)

编辑:如果您希望 Object.entries 默认具有此类型:

declare global {
  interface ObjectConstructor {
    entries<T extends object>(o: T): Entries<T>
  }  
}
Run Code Online (Sandbox Code Playgroud)

  • 是的。全局设置“Object.entries”返回类型的一种方法是拥有一个“.d.ts”文件,其中包含:“import type {Entries} from 'type-fest'声明全局{interface ObjectConstructor {entries&lt;T extends”对象&gt;(o: T): 条目&lt;T&gt; } }` (3认同)

Rob*_*ell 15

我坚信这篇文章的答案应该是本页上两个答案的组合:

type Entries<T> = {
    [K in keyof T]: [K, T[K]];
}[keyof T][];
Run Code Online (Sandbox Code Playgroud)

来自@kaya3

const getEntries = <T extends object>(obj: T) => Object.entries(obj) as Entries<T>;
Run Code Online (Sandbox Code Playgroud)

来自@minlare

谢谢你们两位的精彩回答。

  • 我实际上会添加 `[K in keyof T]-?: [K, T[K]]` 但这正是我使用的。如果没有“-?”,那么使用所有部分属性的对象将返回任何作为键值。 (2认同)

bri*_*out 8

// utils/objectEntries.ts

export { objectEntries }

// /sf/ask/4209937231/#75337277

type ValueOf<T> = T[keyof T]
type Entries<T> = [keyof T, ValueOf<T>][]

// Same as `Object.entries()` but with type inference
function objectEntries<T extends object>(obj: T): Entries<T> {
  return Object.entries(obj) as Entries<T>
}
Run Code Online (Sandbox Code Playgroud)


min*_*are 5

根据 Peter Cardenas 的回答,我还创建了一个辅助函数

const getEntries = <T extends object>(obj: T) => Object.entries(obj) as Entries<T>;
Run Code Online (Sandbox Code Playgroud)