Есть ли способ использовать нулевой оператор объединения (`??`) при деструктуризации свойств объекта?

В 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 ответ

Я вижу два возможных варианта:

  1. Объединяйте нулевое свойство за свойством или

  2. Используйте служебную функцию

Собственность за собственностью

Довольно утомительно (я утомительный разработчик):

      // 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 || {});
    //  ^^^^^^^^^^^^^^^^−−−−−−−−−−−−−−−−−−^
    // ...

Ссылка на игровую площадку

Другие вопросы по тегам