TypeScript: как создать безопасную для типов функцию mapObject? - PullRequest
0 голосов
/ 30 марта 2020

Я создал функцию util, mapObject, которая Array.map, но для объектов.

Она используется примерно так ...

mapObject(
  (value) => value * 100,
  {a: 1, b: 2, c: 3}
)
// {a: 100, b: 200, c: 300}

Вот реализация. У меня работает довольно типобезопасная версия, но мне пришлось замолчать 2 ошибки об использовании obj[key] без подписи индекса.

type ValueOf<T> = T[keyof T];

export const mapObject = <OldObject, NewValue>(
  mappingFn: (value: ValueOf<OldObject>) => NewValue,
  obj: OldObject
): Record<keyof OldObject, NewValue> =>
  Object.keys(obj).reduce((newObj, key) => {
    // @ts-ignore (Not sure how to work around this)
    const oldValue = obj[key];

    // @ts-ignore (Not sure how to work around this)
    newObj[key] = mappingFn(oldValue);

    return newObj;
  }, {} as Record<keyof OldObject, NewValue>);

Какой лучший способ кодировать это? Функция правильно определяет то, что передается, и возвращаемое значение имеет большую безопасность типов. Я бы хотел удалить @ts-ignore s, но без использования индексных сигнатур , так как они наносят ущерб безопасности типов.

Ответы [ 2 ]

1 голос
/ 30 марта 2020

Если вы используете для l oop вместо reduce, тогда TypeScript сможет правильно отследить тип переменной итератора i.

В противном случае, как указал Псидом, Object.keys() возвращает string[], что приводит к потере свойства: сопоставление значений.

Вот минимально измененная версия вашего кода:

    type ValueOf<T> = T[keyof T];

    export const mapObject = <OldObject extends object, NewValue>(
        mappingFn: (value: ValueOf<OldObject>) => NewValue,
        obj: OldObject
    ): Record<keyof OldObject, NewValue> => {
        let newObj = {} as Record<keyof OldObject, NewValue>;
        for (let i in obj) {
            if (obj.hasOwnProperty(i)) {
                const oldValue = obj[i];
                newObj[i] = mappingFn(oldValue);
            }
        }
        return newObj;
    }

Но есть несколько различных способов аннотировать его Для достижения того же результата семантика немного различается.

Вот альтернативная версия, использующая сопоставленные типы (выглядит аналогично, но это не сигнатура индекса).

Это может быть немного более гибкий, потому что входному объекту не нужно , чтобы был типом Record со значениями того же типа.

Реализация такая же, это просто аннотации типов, которые разные:

    type ValueOf<T> = T[keyof T];

    type MapTo<T, U> = {
        [P in keyof T]: U
    }

    function mapObject<T extends object, U>(mappingFn: (v: ValueOf<T>) => U, obj: T): MapTo<T, U> {
        let newObj = {} as MapTo<T, U>;
        for (let i in obj) {
            if (obj.hasOwnProperty(i)) {
                const oldValue = obj[i];
                newObj[i] = mappingFn(oldValue);
            }
        }
        return newObj;
    }

0 голосов
/ 30 марта 2020

Object.keys возврат string[]. Одно из возможных решений - использовать утверждение типа на Object.keys, как это предлагается в этот ответ о переполнении стека :

(Object.keys(obj) as Array<keyof typeof obj>).reduce((newObj, key) => {
...
})

игровая площадка Typescript .

...