gpt4 book ai didi

TypeScript 通用映射可变元组值到嵌套映射类型

转载 作者:行者123 更新时间:2023-12-05 02:39:56 25 4
gpt4 key购买 nike

我正在尝试创建一个辅助函数,它接受像 JSON 这样的嵌套对象,并允许在任意深度深度复制嵌套值。我了解可变元组类型,并且可以让它们为传递元组而工作——但我不知道如何将它们“映射”到任意深度的嵌套 Picks(甚至可能不可能)。这是我想出的最好的 - 但仍然限于需要为 GetNestedValue 创建尽可能多的重载,因为我愿意支持。我理解各种错误,只是想不出任何方法来满足编译器要求并在返回值上完成类型。

// K is arbitrary length how to express N accessors deep? in TS without a loop?
type GetNestedValue<K extends string[], O extends any> = O[K[0]][K[1]][K[2]];

function getNestedItem<Keys extends string[], Obj>(
obj: Obj, ...keys: readonly [...Keys]
): GetNestedValue<Keys, Obj> extends undefined ? undefined : GetNestedValue<Keys, Obj> {
let level: any = obj;
for (const key of keys) {
if (level !== undefined) {
level = level[key];
} else {
return;
}
}

// this will return deepClone(level);
return level;
}


const obj = {one: 1, two: {three: {four: 4}}};

// I'd prefer 'known' shapes of obj here block form entering invalid keys.
const a = getNestedItem(obj, 'one', 'two');

// here - when arbitrarily trying to grab stuff from unknown inputs - I don't want
// a warning, rather the user would just need to check `if (b !== undefined)`
const b = getNestedItem(obj as any, 'one', 'two');

链接到 playground

最佳答案

我首先要说的是:虽然这是一个有趣的思想实验,但我不推荐这样做,因为它需要大量的递归。

它需要两种递归类型,一种是获取从对象类型推断出的一组有效键的类型,另一种是用于访问给定这些经过验证的键的属性的 getter。对于 TypeScript < 4.5,深度限制将是一个长度为 10 的元组。

验证:

// walk through the keys and validate as we recurse. If we reach an invalid
// key, we return the currently validated set along with a type hint
type ValidatedKeys<K extends readonly PropertyKey[], O, ValidKeys extends readonly PropertyKey[] = []> =
K extends readonly [infer Key, ...infer Rest]
// Excluding undefined to allow `a?.b?.c`
? Key extends keyof Exclude<O, undefined>
? Rest extends []
? [...ValidKeys, Key] // case: nothing left in the array, and the last item correctly extended `keyof O`.
: Rest extends readonly PropertyKey[] // obligatory typeguard
? ValidatedKeys<Rest,Exclude<O, undefined>[Key], [...ValidKeys, Key]> // recurse
: never // impossible, we've sufficiently typechecked `Rest`
: [...ValidKeys, keyof Exclude<O, undefined>] // case: key doesn't exist on object at this level, adding `keyof O` will give a good type hint
: [...ValidKeys] // case: empty top level array. This gives a good typehint for a single incorrect string;

setter/getter :

// access a property recursively. Utilizes the fact that `T | never` === `T`
type GetNestedProp<K extends readonly PropertyKey[], O, MaybeUndef extends undefined = never> =
K extends readonly [infer Key, ...infer Rest]
? Key extends keyof O
? Rest extends []
? O[Key] | MaybeUndef // succesful exit, no more keys remaining in array. Union with undefined if needed
/* obligatory typeguard to validate the inferred `Rest` for recursion */
: Rest extends readonly PropertyKey[]
// If it's potentially undefined, We're going to recurse excluding the undefined, and then unify it with an undefined
? O[Key] extends infer Prop
? Prop extends undefined
? GetNestedProp<Rest, Exclude<Prop, undefined>, undefined>
: GetNestedProp<Rest,Prop, MaybeUndef>
: never // impossible, `infer Prop` has no constraint so will always succeed
:never // impossible, we've typechecked `Rest` sufficiently
: undefined // case: key doesn't exist on object at this level
: undefined; // case: empty top level array

为了让函数正确推断泛型,泛型需要作为可能的参数出现。我们要的是ValidKeys ,但如果没有Keys,我们就无法做到这一点。本身作为一个潜在的论点。所以我们为 ...keys 使用条件强制它解决的论点。

关于返回类型,即使GetNestedProp可能与 undefined 联合,编译器无法推断它肯定是在您的 else 分支被命中的情况下。因此,您可以将返回类型设为这种笨拙的条件,或者 //@ts-expect-error else 分支返回语句,返回类型更简单 GetNestedProp<Keys, Obj> .该替代方案包含在 playground 中:

function getNestedItem<Obj, Keys extends readonly [keyof Obj, ...PropertyKey[]], ValidKeys extends ValidatedKeys<Keys, Obj>>(
obj: Obj,
...keys: ValidKeys extends Keys ? Keys : ValidKeys
): GetNestedProp<Keys, Obj> extends undefined ? GetNestedProp<Keys, Obj> | undefined : GetNestedProp<Keys,Obj> {
let level: any = obj;
for (const key of keys) {
if (level !== undefined) {
level = level[key];
} else {
return;
}
}
return level;
}

给定一个具有可选属性的类型,深入研究该属性会将嵌套属性类型转换为具有未定义的联合:

interface HasOpt {
a: { b: number };
aOpt?: {b: number };
}
declare const obj: HasOpt;
const ab = getNestedItem(obj, "a", "b") // number
const abOpt = getNestedItem(obj, "aOpt", "b") // number | undefined

playground

关于TypeScript 通用映射可变元组值到嵌套映射类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68867854/

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