gpt4 book ai didi

typescript - 递归获取对象/记录的属性名称

转载 作者:行者123 更新时间:2023-12-04 08:02:36 30 4
gpt4 key购买 nike

给定形式为 {...} 的对象 - 不是原始类型或数组 - 我想生成一个类型,该类型是该类型中的文字属性名称。我试过用映射类型来做这个,但不能正确。不知道我做错了什么。我可以深入一层(这实际上很容易使用 keyof 运算符),但无法弄清楚如何递归地进行。我知道我很接近,因为在下面的示例中,如果 email属性不是可选的,它可以工作。所以不知何故,它被可选属性绊倒了。

type PropertyNames<
T extends Record<string, unknown>,
MODE extends 'deep' | 'shallow'
> = {
[KEY in keyof T & (string | number )]:
| KEY
| (MODE extends 'deep'
? T[KEY] extends Record<string, unknown>
? PropertyNames<T[KEY], 'deep'>
: never
: never);
}[keyof T & (string | number)];

type Email = {
address: string;
verified: boolean;
};

type UserToken = {
uid: string;
name?: string;
email?: Email;
};

// Expected: uid | name | email | address | verified
// Actual : uid | name | email
type DeepNames = PropertyNames<UserToken, 'deep'>;

// Expected and Actual : uid | name | email
type ShallowNames = PropertyNames<UserToken, 'shallow'>;

最佳答案

我认为您的主要问题是 T[KEY] extends Record<string, unknown> ? ...不是 distributiveT[KEY] 中可能的联合,等等当 KEY是可选属性,T[KEY]将包括 undefined ,以及自 undefined extends Record<string, unknown>不是真的,整个事情都失败了,你会得到 never .最好使用分布在工会上的公式。
我的倾向是做这样的事情:

type PropertyNames<T, M extends 'deep' | 'shallow'> =
T extends object ?
{ [K in keyof T]-?: K |
(M extends 'deep' ? PropertyNames<T[K], "deep"> : never)
}[keyof T] : never
或者,如果您在其中混合了数组类型,则:
type PropertyNames<T, M extends 'deep' | 'shallow'> =
T extends object ?
{ [K in keyof T]-?: K |
(M extends 'deep' ? PropertyNames<T[K], "deep"> : never)
}[Extract<keyof T, T extends readonly any[] ? number : unknown>] : never
这为您的示例产生了这一点:
type DeepNames = PropertyNames<UserToken, 'deep'>;
// type DeepNames = "uid" | "name" | "email" | "address" | "verified"
如预期的。

我所做的改变:
  • 我已经放弃了 Record<string, unknown>对于问题较少的 object , 自 interface没有索引签名的类型往往不能分配给 Record<string, unknown> :
    interface Oops {
    x: string;
    }

    type Nope = Oops extends Record<string, unknown> ? "yep" : "nope";
    // type Nope = "nope"
  • 我已经取消了对 T 的限制是一个对象类型;这使得在 PropertyNames<T[K]> 上进行递归评估变得更容易。 ;如果 T不是对象则返回 never .这也会自动将操作分配给 T 中的联合。 .虽然 T[K] extends ...不分发,T extends ...是因为 T是一个“裸”类型参数。
  • 我已经使用 the -? modifier 使映射类型将所有属性转换为所需的属性。 ;这将消除任何奇怪的undefined s 可能出现在最终输出中。
  • 我已更换 keyof T & (string | number)keyof T因为除非我们试图禁止 symbol键我认为这并不重要。
  • Extract<keyof T, T extends readonly any[] ? number : unknown>的事只是处理数组;你大概不想看到数组方法的所有名称,比如 "push""pop"在你的输出中(如果你这样做,你可以使用 keyof T 代替)。因此,对于数组,只需查看数字索引处的属性。

  • Playground link to code

    关于typescript - 递归获取对象/记录的属性名称,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66379051/

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