Ввод функции, которая сопоставляет свойство в объекте с другим свойством

#typescript #typescript-typings #typescript-generics

#typescript #typescript-типизации #typescript-generics

Вопрос:

Я пытаюсь ввести функцию, которая сопоставляет свойство в объекте с другим свойством.

Например mapProperty , функция может использоваться как . mapProperty('_id', 'id', { _id: "arst" } ) Результирующий тип должен быть { id: string } .

Я создал функцию mapProperty , указанную ниже, но я чувствую as unknown as M , что это не обязательно. Однако, когда я не использую это, я получаю следующую ошибку:

Тип 'T amp; { [x: string]: T[K]; }' не может быть присвоен типу 'M' .
'M' может быть создан экземпляр с произвольным типом, который может быть не связан с 'T amp; { [x: string]: T[K]; }' .

Также невозможно ввести возвращаемое значение как : { [key: L]: T[K] } .

Каким может быть решение?

 const mapProperty = <T, K extends keyof T, M, L extends keyof M>(
  from: K,
  to: L,
  obj: T,
): M => {
  const prop = obj[from];
  delete obj[from];
  return ({ ...obj, [to]: prop } as unknown) as M;
};
  

Редактировать: (Я использовал ответ Николай Гольцев с изюминкой)

Я добавил параметр, представляющий собой функцию, которая сопоставляет значение сопоставленного свойства:

 const mapProperty = <T, K extends keyof T, L extends string, M = T[K]>(
  obj: T,
  from: K,
  to: L,
  fn?: (x: T[K]) => M,
): Omit<T, K> amp; { [key in L]: M } => {
  const prop = fn ? fn(obj[from]) : obj[from];
  delete obj[from];
  return { ...obj, [to]: prop } as any;
};
  

Ответ №1:

Его можно ввести следующим образом:

 const mapProperty = <T, K extends keyof T, L extends string>(
  from: K,
  to: L,
  obj: T,
): Omit<T, K> amp; {
    [key in L]: T[K]
} => {
  const prop = obj[from];
  delete obj[from];
  return { ...obj, [to]: prop } as any;
};

const k = mapProperty('_id', 'id', { _id: "arst", k: true } )
  

Ошибка возникает в операторе return, потому delete что оператор ничего не делает с типом переменной. После операции удаления это все равно будет T . Вычисляемое свойство, как я знаю, всегда дает тип { [x: string]: T } .

Ответ №2:

Пожалуйста, взгляните на следующее решение:

 
type Data = { age: 1, name: 'string' }

// Main utility
type Replace<O, P1 extends keyof O, P2 extends string> =
    Pick<O, Exclude<keyof O, P1>>
    amp; { [P in P2]: O[P1] }


type Result = Replace<Data, 'name', 'surname'>

const mapProperty = <O, P1 extends keyof O, P2 extends string>(obj: O, from: P1, to: P2): Replace<O, P1, P2> => {
    const { [from]: replaced, ...rest } = obj;

    // don't know how to get rid of casting operators
    return {
        ...rest,
        [to]: replaced
    } as unknown as Replace<O, P1, P2>
}

const data: Data = { age: 1, name: 'string' }

const result = mapProperty(data, 'age', 'year')
type Res = keyof typeof result // "year" | "name"
  

Считается хорошей практикой вообще не использовать delete operator , потому что он медленный и имеет другие недостатки. В качестве альтернативы вместо этого можно использовать Reflect.deleteProperty() . Я не знаю, как избавиться unknown от приведения из-за вычисляемой природы [to] .