gpt4 book ai didi

typescript - 在 Typescript 中使用自定义键展平对象

转载 作者:行者123 更新时间:2023-12-05 00:54:19 27 4
gpt4 key购买 nike

我想扁平化对象并将返回值转换为一种类型。

例如:

const myObject = {
names: {
title: 'red',
subtitle: 'green'
},
}
const returned = [...Object.values(flatten(myObject))] as const
// returns ['red', 'green']
type Type = typeof returned[number]

返回的变量现在是 ['red', 'green']

类型应该是'red | 'green',但现在是一个字符串数组,因为返回的 typeof 是 string[]。我想使用这种类型为我的组件键入 prop:

<Component name="red" /> //is correct, but
<Component name=`something different than "red" or "green"` /> //is incorrect.

扁平化功能:

type FlattenedObject = { [x: string]: string }

export const flattenDaisyChain = (obj: any): FlattenedObject => {
const result: FlattenedObject = {}

const transform = (wrapper: FlattenedObject | string, p?: string) => {
switch (typeof wrapper) {
case 'object':
p = p ? p + '.' : ''
for (const item in wrapper) {
transform(wrapper[item], p + item)
}
break
default:
if (p) {
result[p] = wrapper
}
break
}
}

transform(obj)

return result
}

最佳答案

首先,如果你想让编译器意识到 Type"red"| "green" 相对于 string,我们必须确保 myObjct 的类型包括这些 literal types .最简单的方法是使用 const assertion :

const myObject = {
names: {
title: 'red',
subtitle: 'green'
},
} as const;

接下来,您的 flatten() 函数的类型在 TypeScript 4.1+ 中几乎无法表示。我可能会这样写,使用 similar question 的答案:

type Flatten<T extends object> = object extends T ? object : {
[K in keyof T]-?: (x: NonNullable<T[K]> extends infer V ? V extends object ?
V extends readonly any[] ? Pick<T, K> : Flatten<V> extends infer FV ? ({
[P in keyof FV as `${Extract<K, string | number>}.${Extract<P, string | number>}`]:
FV[P] }) : never : Pick<T, K> : never
) => void } extends Record<keyof T, (y: infer O) => void> ?
O extends infer U ? { [K in keyof O]: O[K] } : never : never

const flatten = <T extends object>(obj: T): Flatten<T> => { /* impl */ }

它使用 recursive conditional types , template literal types , 和 key remapping in mapped types递归遍历所有属性并将键与 . 连接在一起。我不知道这个特定类型的函数是否适用于 flatten() 的所有用例,我不敢假装它没有潜伏在其中的恶魔。


如果我使用那个签名,并用上面的 const-asserted myObject 调用 flatten(myObject),你会得到这个:

const flattenedMyObject = flatten(myObject);
/* const flattenedMyObject: {
readonly "names.title": "red";
readonly "names.subtitle": "green";
} */

万岁!这足以让 Type 成为 "red"| “绿色”:

const returned = [...Object.values(flatten(myObject))] as const
type Type = typeof returned[number] // "red" | "green"

也万岁。


我想如果你真的不关心跟踪键名,你可以通过返回 string index signature 来大大简化 flatten() 类型签名。 :

type DeepValueOf<T> = object extends T ? object :
T extends object ? DeepValueOf<
T[Extract<keyof T, T extends readonly any[] ? number : unknown>]
> : T

const flatten = <T extends object>(obj: T): { [k: string]: DeepValueOf<T> } => {
/* impl */ }

产生

const flattenedMyObject = flatten(myObject);
/* const flattenedMyObject: {
[k: string]: "green" | "red";
} */

最终

const returned = [...Object.values(flatten(myObject))] as const
type Type = typeof returned[number] // "red" | "green"

我对 DeepValueOf 的担心不如对 Flatten 的担心,但如果没有一些边缘情况,我会感到惊讶。因此,在任何类型的生产代码中使用它之前,您都需要进行真正的测试。

Playground link to code

关于typescript - 在 Typescript 中使用自定义键展平对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66614528/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com