简体   繁体   English

类型“未定义”不能用作使用泛型类型的索引类型

[英]Type 'undefined' cannot be used as an index type using Generic type

Am trying to type a function that update a pair key value by a given path, am using Generics type for the object, because i do not know what type in advance and ive using also Generic type for the parameter value, but i can't figure out why ive got an error message: Type 'undefined' cannot be used as an index type and No overload matches this call :我正在尝试键入通过给定路径更新对键值的 function,我对 object 使用 Generics 类型,因为我不知道预先知道什么类型的值,但也可以使用参数找出为什么我收到一条错误消息:类型'undefined' cannot be used as an index type并且No overload matches this call

const updateObjetValueByPath = <A, B>(obj: { [key: string]: A }, path: string, value: B) => {
  const keys = path.split('.')
  const lastKey = keys.pop()
  const lastObj = keys.reduce((obj, key) => obj[key], obj)
  lastObj[lastKey] = value
}

Example:例子:

const obj = {
  firstName:'Taha',
  lastName: 'Az',
  work:{
    company:'Whitoo',
    contrat:{
      lib:'CDI',
    }
  }
}
updateObjetValueByPath(obj,'work.contrat.lib','Freelance')

在此处输入图像描述

Type signature of your function is not safe, it allows you to use any path, even foo.bar.baz fro any object, which is not safe.您的 function 的类型签名是不安全的,它允许您使用任何路径,甚至foo.bar.baz来自任何 object,这是不安全的。

Please see this example:请看这个例子:


type Foo = {
  user: {
    description: {
      name: string;
      surname: string;
      age: number
    }
  }
}

declare var foo: Foo;

type Primitives = string | number | symbol;

type Values<T> = T[keyof T]

type Elem = string;

type Acc = Record<string, any>

// (acc, elem) => hasProperty(acc, elem) ? acc[elem] : acc
type Predicate<Accumulator extends Acc, El extends Elem> =
  El extends keyof Accumulator ? Accumulator[El] : Accumulator

type Reducer<
  Keys extends Elem,
  Accumulator extends Acc = {}
> =
  Keys extends `${infer Prop}.${infer Rest}`
  ? Reducer<Rest, Predicate<Accumulator, Prop>>
  : Keys extends `${infer Last}`
  ? Predicate<Accumulator, Last>
  : never


const hasProperty = <Obj, Prop extends Primitives>(obj: Obj, prop: Prop)
  : obj is Obj & Record<Prop, any> =>
  Object.prototype.hasOwnProperty.call(obj, prop);

type KeysUnion<T, Cache extends string = ''> =
  T extends Primitives ? Cache : {
    [P in keyof T]:
    P extends string
    ? Cache extends ''
    ? KeysUnion<T[P], `${P}`>
    : Cache | KeysUnion<T[P], `${Cache}.${P}`>
    : never
  }[keyof T]

type O = KeysUnion<Foo>

type ValuesUnion<T, Cache = T> =
  T extends Primitives ? T : Values<{
    [P in keyof T]:
    | Cache | T[P]
    | ValuesUnion<T[P], Cache | T[P]>
  }>


function updateObjetValueByPath<Obj, Keys extends KeysUnion<Obj>>
  (obj: ValuesUnion<Obj>, path: Keys & string, value: Reducer<Keys, Obj & Acc>) {
  const keys = path.split('.')
  const lastKey = keys.pop()!
  const lastObj = keys.reduce((o, key) => o[key], obj as Record<string, {}>)
  lastObj[lastKey] = value
}

/**
 * Ok
 */

const result1 = updateObjetValueByPath(foo, 'user.description.name', 'John') // ok
const result2 = updateObjetValueByPath(foo, 'user.description.age', 2) // ok

/**
 * Expected errors
 */
const result3 = updateObjetValueByPath(foo, 'surname', 'Doe') // expected error, path is not full
const result4 = updateObjetValueByPath(foo, 'user.description.name', 42) // expecte derror, it should be a string

Playground操场

If you are interested in explanation please see my article and my answer1 , answer2 , answer3, answer4如果你有兴趣解释请看我的文章和我的answer1 , answer2 , answer3, answer4

As for missing link, please see links on the very bottom of my article https://catchts.com/deep-pick .至于缺少的链接,请参阅我的文章https://catchts.com/deep-pick 最底部的链接。

PS "add comment" button does not work on my mobile phonr PS“添加评论”按钮在我的手机上不起作用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM