很长一段时间我们遇到的问题是,安全轻松地访问嵌套属性的唯一方法是使用_.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
解构数组(其中T[number]
是数组)。问题是我没有办法约束嵌套属性上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
这实际上在调用站点工作(没有错误,正确地给我们param和返回类型),但是在声明本身中给我们一个错误,因为你不能用{{TObject[P1]
索引[number]
1}}我们无法保证TObject[P1]
是一个数组。
(注意:TType[number]
是一种从数组类型中获取元素类型的可行方法,但我们需要说服编译器我们对数组执行此操作 )
问题是,是否会有一个数组约束添加到TObject[P1]
或还有另一种方法可以解决这个问题。
答案 0 :(得分:4)
我已经想出来并在这里发布了一个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
库中已经生成了数百种数组和对象属性组合,并且可以随时使用,但我仍然愿意以通用的方式改进这一点,以便我们可以使用同一声明中的动态参数数量。甚至可以将Array和Property导航组合到同一类型约束中,这样我们就不必生成数组和属性访问的每个变体。