lodash 的类型安全版本 _.get - 有条件地解构数组类型

Type safe version of lodash _.get - deconstruct array type conditionally

很长一段时间以来,我们一直遇到一个问题,即安全、轻松地访问嵌套 属性 的唯一方法是使用 _.get。例如:

_.get(obj, "Some.Nested[2].Property", defaultValue);

这很好用,但经不起经常发生的 属性 重命名。从理论上讲,应该可以将上面的内容转换为下面的内容,并允许 TypeScript 对其进行隐式类型检查:

safeGet(obj, "Some", "Nested", 2, "Property", defaultValue);

我已经成功地为除数组类型之外的所有内容创建了这样的类型:

function getSafe<TObject, P1 extends keyof TObject>(obj: TObject, p1: P1): TObject[P1];

function getSafe<TObject, P1 extends keyof TObject, P2 extends keyof TObject[P1]>(obj: TObject, p1: P1, p2: P2): TObject[P1][P2];

这会正确地检查项目的深度(我将自动生成这些语句到 10 级左右)。它因数组属性而失败,因为传递给下一个参数的类型是 T[] 而不是 T.

无需考虑任何解决方案的复杂性或冗长性,因为代码将自动生成,问题是我似乎无法找到允许我接受整数参数和向前解构数组类型。

您可以使用 T[number] 解构一个数组(其中 T 是一个数组)。问题是我无法限制 T 是嵌套 属性.

上的数组的位置
function getSafe<TObject, P1 extends keyof TObject, P2 extends keyof TObject[P1][number]>(obj: TObject, p1: P1, index: number, p2: P2): TObject[P1][number][P2];
                                                                                 ^^^^^^                                                             ^^^^^^
const test2 = getSafe(obj, "Employment", 0, "Id"); // example usage

这实际上在调用站点有效(那里没有错误,正确地给了我们参数和 return 类型),但在声明本身中给了我们一个错误,因为你不能索引 TObject[P1][number] 因为我们不能保证 TObject[P1] 是一个数组。

(注意:TType[number] 是从数组类型中获取元素类型的可行方法,但我们需要让编译器相信我们正在对数组执行此操作)

真正的问题是,是否可以将数组约束添加到 TObject[P1] 是否有另一种我缺少的方法来执行此操作。

我已经解决了这个问题并在这里发布了一个 npm 包:ts-get-safe

关键是弄清楚如何有条件地 将数组重组为其元素类型。为此,您首先必须断言所有属性都是数组或 never。求解方程式的类型是:

type GSArrEl<TKeys extends keyof TObj, TObj> = { [P in TKeys]: undefined[] & TObj[P] }[TKeys][number];

神奇之处在于 { [P in TKeys]: undefined[] & TObj[P] },我们基本上将 TObj 的每个 属性 联合到 undefined[]。因为我们确定每个 属性 要么是一个数组,要么是 never(每个 属性 上的 never 不是数组),然后我们可以执行解构表达式 [number] 以获取元素类型。

这里是两个数组解构同时发生的例子:

function getSafe<TObject, P0 extends keyof TObject, A1 extends GSArrEl<P0, TObject>, P2 extends keyof A1, P3 extends keyof A1[P2], A4 extends GSArrEl<P3, A1[P2]>>(obj: TObject, p0: P0, a1: number, p2: P2, p3: P3, a4: number): A4;

在我的 ts-get-safe 库中生成了数百种数组和对象属性的组合并可以使用,但是我仍然愿意以通用的方式改进它,以便我们可以使用同一声明中参数的动态数量。甚至是一种将数组和 属性 导航组合到同一类型约束中的方法,这样我们就不必生成数组和 属性 访问的每个变体。