Безопасное преобразование одного объекта в другой (он же выбор)

#typescript #typescript-generics

#typescript #typescript-дженерики

Вопрос:

У меня много объектов с известными типами, и мне нужно преобразовать эти объекты в другие объекты с известными типами, подобными этому:

 interface FruitesWithNoCase = {
  brokenbanana: string;
  brokenorange: string;
  apple: number;
}

interface FruitesWithCorrectCase = {
  brokenBanana: string;
  brokenOrange: string;
}

const inputObject: FruitesWithNoCase = { // My input data
  brokenbanana: 'foo',
  brokenorange: 'bar',
  apple: 123
}

const outputObject: FruitesWithCorrectCase = { // Desired result
  brokenBanana: 'foo',
  brokenOrange: 'bar'
}
  

Я не хочу сопоставлять каждый объект вручную, и было бы здорово иметь какую-то функцию, которая может принимать входной объект типа X и массив свойств, а также возвращать и выводить объект типа Y, что-то вроде:

 const outputObject = getAttributes<FruitesWithNoCase, FruitesWithCorrectCase, (keyof FruitesWithCorrectCase)[]>(inputObject, ['brokenBanana', 'brokenOrange']);
  
  1. У меня всегда есть типы ввода и вывода
  2. У меня всегда есть список свойств, которые мне нужно выбрать в camelCase
  3. Я всегда уверен, что output[prop.toLowerCase()] существует в input

Без типов рабочий код выглядит следующим образом:

 const inputObject = {
  "brokenbanana": 'foo',
  "brokenorange": 'bar',
  "apple": 123
}

const anotherInputObject = {
  "randomcar": "Fiat",
  "randomtruck": "Ford"
}

function getAttributes(obj, keys) {
  return keys.reduce((acc, key) => {
    acc[key] = obj[key.toLowerCase()]
    return acc;
  }, {});  
}

const outputObject = getAttributes(inputObject, ['brokenBanana', 'brokenOrange']);
const anotherOutputObject = getAttributes(anotherInputObject, ['randomCar'])

console.log(outputObject); // { brokenBanana: 'foo', brokenOrange: 'bar' }
console.log(anotherOutputObject); // { randomCar: 'Fiat' }
  

Пожалуйста, помогите сделать эту функцию безопасной для ввода. Спасибо.

Ответ №1:

Сработает ли что-то подобное?

 function getAttributes<T extends string>(obj: Record<string, string> , keys: T[]): Record<T, string> {
  return keys.reduce((acc, key) => {
    acc[key] = obj[key.toLowerCase()]
    return acc;
  }, {} as Record<T, string>);  

}
  

Ответ №2:

На самом деле вы не можете вводить ключи объекта в нижний регистр и ожидать, что он когда-либо будет типобезопасным. Если вы хотите полностью сохранить тип, приведенное ниже, вероятно, лучшее, что вы получите.

 const inputObject = { // My input data
    brokenbanana: 'foo',
    brokenorange: 'bar',
    apple: 123
}

const outputObject = move(inputObject, ["brokenbanana", "brokenBanana"], ["brokenorange", "brokenOrange"])

function move<T, Old extends keyof T, New extends keyof any>(obj: T, ...map: Array<[Old, New]>): Record<New, T[Old]> {
    const copy = {} as Record<New, T[Old]>;
    map.forEach(([o, n]) => copy[n] = obj[o])
    return copy
}