Есть ли способ использовать нулевой оператор объединения (`??`) при деструктуризации свойств объекта?
В ReactJS я обычно использую этот шаблон свойств destructurnig (я полагаю, это довольно идиоматично):
export default function Example({ ExampleProps }) {
const {
content,
title,
date,
featuredImage,
author,
tags,
} = ExampleProps || {};
Я могу добавлять значения по умолчанию во время деструктуризации, что добавляет некоторой безопасности:
export default function Example({ ExampleProps }) {
const {
content = "",
title = "Missing Title",
date = "",
featuredImage = {},
author = {},
tags = [],
} = ExampleProps || {};
Но теперь я перешел на TypeScript
strict
режим и мне довольно тяжело. Мои реквизиты набраны с помощью кодогенератора GraphQl, и практически все свойства заключены в
Maybe<T>
типа, поэтому в развернутом виде
actualValue | null | undefined
.
Значения по умолчанию
({ maybeUndefined = ""} = props)
может спасти меня в случае, если ценность
undefined
, но
null
значения будут пропадать, поэтому компилятор TS ворчит, и мой код приводит к большому количеству:
tags?.nodes?.length // etc…
что заставляет меня немного нервничать из-за статьи о затратах на необязательную цепочку (хотя я не знаю, насколько она актуальна в 2021 году). Я также слышал
?.
чрезмерное использование оператора рассматривается как пример "запаха кода".
Есть ли шаблон, вероятно, использующий
??
оператор, который сделает компилятор TS счастливым И сможет отсеять хотя бы часть этого
very?.long?.optional?.chains
?
1 ответ
Я вижу два возможных варианта:
Объединяйте нулевое свойство за свойством или
Используйте служебную функцию
Собственность за собственностью
Довольно утомительно (я утомительный разработчик):
// Default `ExampleProps` here −−−−−−−−−−−−−−−vvvvv
export default function Example({ ExampleProps = {} }) {
// Then do the nullish coalescing per-item
const content = ExampleProps.content ?? "";
const title = ExampleProps.title ?? "Missing Title";
const date = ExampleProps.date ?? "";
const featuredImage = ExampleProps.featuredImage ?? {},
const author = ExampleProps.author ?? {},
const tags = ExampleProps.tags ?? [];
// ...
Вспомогательная функция
В качестве альтернативы используйте служебную функцию в этих строках для преобразования
null
значения (как во время компиляции, так и во время выполнения) для
undefined
, так что вы можете использовать деструктурирование по умолчанию при деструктурировании результата. Типовая часть довольно проста:
type NullToUndefined<Type> = {
[key in keyof Type]: Exclude<Type[key], null>;
}
Тогда функция полезности может быть примерно такой:
function nullToUndefined<
SourceType extends object,
ResultType = NullToUndefined<SourceType>
>(object: SourceType) {
return Object.fromEntries(
Object.entries(object).map(([key, value]) => [key, value ?? undefined])
) as ResultType;
}
или так (вероятно, более эффективно с точки зрения времени выполнения):
function nullToUndefined<
SourceType extends object,
ResultType = NullToUndefined<SourceType>
>(object: SourceType) {
const source = object as {[key: string]: any};
const result: {[key: string]: any} = {};
for (const key in object) {
if (Object.hasOwn(object, key)) {
result[key] = source[key] ?? undefined;
}
}
return result as ResultType;
}
Обратите внимание, что
Object.hasOwn
очень новый, но легко полифиллируемый . Или вы могли бы использовать
Object.prototype.hasOwn.call(object, key)
вместо.
(в обоих случаях в пределах
nullToUndefined
Я играю немного быстро и свободно с утверждениями типов. Я думаю, что для такой небольшой полезной функции это разумный компромисс при условии, что входы и выходы четко определены.)
Затем:
export default function Example({ ExampleProps }) {
const {
content = "",
title = "Missing Title",
date = "",
featuredImage = {},
author = {},
tags = [],
} = nullToUndefined(ExampleProps || {});
// ^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−^
// ...