Безопасное преобразование типов шрифтов между двумя типами - PullRequest
0 голосов
/ 23 апреля 2020

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

Проблема

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

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

Пример:

interface SharableData {
    id: string;
    name: string;
    description: string;
    type: DataType;
    priority: number;
    //...
    calculatedValue: number;
}

interface SensitiveData extends Omit<SharableData, 'calculatedValue'> {
    dontShareThis: string
}

const storedSensitive: SensitiveData = getFromStorage<SensitiveData>();

// Do something with the data and return a type safe SharableData 

Очевидно, что «простой» способ сделать это - создать fre sh новый объект SharableData и присваивать свойства одно за другим.

const result: SharableData = {
    id: storedSensitive.id;
    name: storedSensitive.name;
    description: storedSensitive.description;
    type: storedSensitive.type;
    priority: storedSensitive.priority;
    //...
    calculatedValue: calculateValue(storedSensitive);
}

return result;

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

1 Ответ

0 голосов
/ 23 апреля 2020

Решение на данный момент

Сопоставленный тип Undefined, позволяющий передавать набор свойств, соответствующих типу, но без значения:

/**
 * Map type to undefined values
 */
type Undefined<T> = {
    [P in keyof T]: undefined;
};

Метод removePropToType, который вынуждает меня передать объект с нештатными свойствами SensitiveData

/**
 * Given a types T1 and T2, and an object `base` of type `T1 & T2`,
 * force you to pass a toRemove value that will lead to a pure T2 type object.
 * Warning: Works of first level only.
 * @example
 * removePropToType<PrivateUser, PublicUser>(privateUser, { sensibleProp: undefined, hiddenProp: undefined })
 * @param base The object to cleanup, which should be T1 & T2
 * @param toRemove An object containing undefined value, for each properties of T1 that is not in T2
 * @returns The base object, but with all property from `toRemove` deleted, therefore a T2 object.
 */
export function removePropToType<T1, T2>(base: T1 & T2, toRemove: Undefined<Omit<T1, keyof T2>>): T2 {
    const copy = { ...base };
    Object.keys(toRemove).forEach(key => {
        delete copy[key as keyof T1];
    });
    return copy;
}

Затем я могу:

const result: SharableData & SensitiveData = {
    ... storedSensitive,
    calculatedValue: calculateValue(storedSensitive)
}

return removePropToType<SensitiveData, SharableData>(result, { dontShareThis: undefined });

Здесь, если я добавлю или удалю чувствительные свойства в интерфейсе SensitiveData, которых нет в SharableData , компилятор выдаст ошибку.

Я наполовину доволен тем, что мне все еще нужно передать неопределенный объект, но это разумно решило мою проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...