打字稿:使用元组作为索引类型

马克西米利安·梅林格

给定的是一些像这样的键的元组["a", "b", "c"]和带有这些键作为属性的嵌套对象{a: {b: {c: number}}}您如何递归地使用元组的成员作为打字稿中的索引?

没有正确键入的实现:

function recursivePluck(ob: any, tuple: any[]): any {
  for (let index of tuple) {
    ob = ob[index]
  }
  return ob
}

您如何键入上面的代码?

我尝试了以下

type RecursivePluck<
  Tuple extends string[], 
  Ob extends {[key in string]: any}, 
  TupleWithoutFirst extends SliceStartQuantity<Tuple, 1> = SliceStartQuantity<Tuple, 1>
>

= TupleWithoutFirst extends [] ? Ob[Tuple[0]] : RecursivePluck<TupleWithoutFirst, Ob[Tuple[0]]>

但是这个错误 Type alias 'RecursivePluck' circularly references itself.

请注意,SliceStartQuantity来自typescript-tuplenpm

马西耶·西科拉(Maciej Sikora)

在这里,解决方案涵盖了参数和返回类型的类型安全性:

type Unshift<A, T extends Array<any>> 
= ((a: A, ...b: T) => any) extends ((...result: infer Result) => any) ? Result : never;
type Shift<T extends Array<any>> 
= ((...a: T) => any) extends ((a: any, ...result: infer Result) => any) ? Result : never;

type Revert
  <T extends Array<any>
  , Result extends Array<any> = []
  , First extends T[keyof T] = T[0]
  , Rest extends Array<any> = Shift<T>> = {
  [K in keyof T]: Rest['length'] extends 0 ? Unshift<First, Result> : Revert<Rest, Unshift<First, Result>> 
}[0]

// this was done to avoid infinite processing the type by TS
type Level = 0 | 1 | 2 | 3 | 4 | 5
type NextLevel<X extends Level> = 
  X extends 0
  ? 1
  : X extends 1
  ? 2
  : X extends 2
  ? 3
  : X extends 3
  ? 4
  : X extends 4
  ? 5
  : never

// this type will give us possible path type for the object
type RecursivePath<Obj extends object, Result extends any[] = [], Lv extends Level = 0> = {
  [K in keyof Obj]: 
    Lv extends never
    ? Result
    : Obj[K] extends object 
    ? (Result['length'] extends 0 ? never : Revert<Result>) | RecursivePath<Obj[K], Unshift<K, Result>, NextLevel<Lv>>
    : Revert<Result> | Revert<Unshift<K,Result>>
}[keyof Obj]

// checks if type is working
type Test = RecursivePath<{a: {b: {c: string}, d: string}}>
type Test2 = RecursivePath<{a: {b: {c: {e: string}}, d: string}}>

// this type will give as value type at given path
type RecursivePathValue<Obj, Path extends any> = 
{
  [K in keyof Path]: 
    Path extends any[]
    ? Path[K] extends keyof Obj 
    ? Path['length'] extends 1 
    ? Obj[Path[K]]
    : RecursivePathValue<Obj[Path[K]], Shift<Path>>
    : never
    : never
}[number]

// checks if type is working
type Test3 = RecursivePathValue<{a: {b: {c: string}, d: string}},['a', 'b']>
type Test4 = RecursivePathValue<{a: {b: {c: {e: string}}, d: string}}, ['a','d']>

// finnaly the function
function recursivePluck<Obj extends object, Path extends RecursivePath<Obj>>(ob: Obj, tuple: Path): RecursivePathValue<Obj, Path> {
  // inside I just fallback to any
  let result: any = ob;
  for (let index of tuple as any[]) {
    result = result[index]
  }
  return result;
}
const a = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b']) // ok
const b = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','e']) // error
const c = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b','e']) // error
const d = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b','c']) // ok
const e = recursivePluck({a: {b: {c: {d: 'value'}}}}, ['a','b','c', 'd']) // ok

操场

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

打字稿-混合类型

来自分类Dev

如何使用类型防护解决接口的打字稿联合类型

来自分类Dev

打字稿联合类型

来自分类Dev

打字稿:扩展接口时类型中缺少索引签名

来自分类Dev

打字稿:类型中缺少索引签名

来自分类Dev

打字稿:元组到联合类型

来自分类Dev

打字稿合并类型

来自分类Dev

打字稿:onPress类型

来自分类Dev

使用Jest作为打字稿中的方法

来自分类Dev

在打字稿中将枚举类型作为参数传递

来自分类Dev

如何在打字稿中显式定义具有不同元组类型的元组数组?

来自分类Dev

打字稿推断的类型不能使用

来自分类Dev

枚举作为打字稿中的通用类型

来自分类Dev

在yup模式中使用打字稿类型

来自分类Dev

元组镇的打字稿麻烦:保留参数类型

来自分类Dev

react-admin打字稿:不能使用名称空间作为类型

来自分类Dev

联合类型的部分键作为打字稿中对象的键

来自分类Dev

什么是打字稿中的可索引类型?

来自分类Dev

打字稿超级类型

来自分类Dev

将useReducer分发作为打字稿中的道具,要使用哪种类型?

来自分类Dev

const变量作为类型-打字稿

来自分类Dev

打字稿:使用元组作为rest参数有什么意义?

来自分类Dev

如何在打字稿中指定枚举的索引类型

来自分类Dev

使用元组/列表作为数组索引(Python)

来自分类Dev

使用const作为打字稿中的对象和类型?

来自分类Dev

使用打字稿推断类型的问题

来自分类Dev

打字稿包装类型

来自分类Dev

打字稿类型断言

来自分类Dev

Promise 的打字稿类型