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

Машинопись «?» оператор означает, что параметр является необязательным, его необязательно передавать в компоненте / функции.

Не определено означает, что параметр необходимо передать, но его значение может быть неопределенным.

Вы не должны использовать «?» в качестве замены undefined и наоборот, даже если «?» означает, что параметр может отсутствовать и, следовательно, будет неопределенным для компилятора Typescript (я хочу кратко упомянуть здесь, что необязательный параметр не является нулевым значением, он либо существует, либо не определен, если не передан).

Давайте посмотрим на пример с необязательным параметром:

interface UserProps {
  name: string,
  age?: string
}

const getUserData = ({name, age} : UserProps) => {
  if(age) {
    return `User ${name} is ${age}.`;
  }
  return `User ${name} decided not to tell his/her age.`;
}
getUserData({ name: 'Mark' });

Мне не нужно передавать age в функцию getUserData, если я не хочу, но я также могу забыть об этом, потому что Typescript не выделяет недостающие необязательные параметры. В более сложных случаях другим разработчикам приходится читать весь код компонента, чтобы понять, как он работает, и соответствующим образом передавать данные.

В дополнение к этому у нас может возникнуть еще одна проблема: если мы вообще удалим логику «возраста» из функции getUserData - легко забыть удалить «возраст» из типа UserProps (особенно если он импортирован из другого файла), потому что Typescript принимает его как допустимый вариант:

interface UserProps {
  name: string,
  age?: string
}
const getUserData = ({name} : UserProps) => {
  return `User's name is ${name}`;
}
getUserData({ name: 'Mark' });

Теперь давайте посмотрим на ту же функцию, но с явно указанным неопределенным параметром:

interface UserProps {
  name: string,
  age: string | undefined
}
const getUserData = ({name, age} : UserProps) => {
  if(age) {
    return `User ${name} is ${age}.`;
  }
  return `User ${name} decided not to tell his/her age.`;
}
getUserData({ name: 'Mark', age: undefined });

В приведенном выше примере Typescript заставит меня передать ключ «age» в функцию getUserData (мне даже не нужно заглядывать внутрь компонента, чтобы узнать об этом) и не позволит мне забыть удалить «age» из UserProps, если Я повторно реализую функцию getUserData:

interface UserProps {
  name: string
}
const getUserData = ({name} : UserProps) => {
  return `User's name is ${name}`;
}
getUserData({ name: 'Mark' });

Третья проблема с необязательным "?" Параметр заключается в том, что когда люди используют его в интерфейсе, они должны писать код, который действительно работает без этого параметра, и они довольно часто забывают об этом. Я заметил, что по какой-то причине разработчики склонны писать проверки в коде, если они явно определили параметр как undefined, при этом забывая о проверке, помещают ли они параметр как необязательный.

Bad code:
interface MyButtonProps {
  onClick: () => void;
  text?: string;
  isSubmit?: boolean;
}
const MyButton: React.FC<MyButtonProps> = ({ onClick, text, isSubmit }) => {
  return (
    /* Developer didn't handle the case when flag is passed as true but text is not passed */
    <button onClick={onClick}>{isSubmit ? text : 'Cancel'}</button>;
  )
};

Последняя проблема машинописного текста, необязательный «?» параметры в том, что людям нравится использовать его на всякий случай ... ну знаете, чтобы сделать компонент более гибким в будущем. Обычно происходит то, что они делают его более открытым для ошибок и ошибочных предположений, а не гибкости. Есть несколько общих правил использования необязательных параметров в коде:

  1. Используйте необязательные параметры, только если они действительно необязательны, и компонент будет работать должным образом, если пользователь его не передаст. Если вы хотите, вы можете попробовать использовать свой компонент самостоятельно, не передавая необязательную опору, и посмотреть, как он себя ведет.
  2. Если вам нужно использовать необязательные параметры, убедитесь, что ваш компонент не сломается (напишите защитный код, например, проверяйте, переданы ли необязательные параметры). Стоит отметить, что компонент становится беспорядочным и нечитаемым, если у вас много защитного кода:
OK code:
interface MyButtonProps {
  onClick: () => void;
  text?: string;
  isSubmit?: boolean;
}
const MyButton: React.FC<MyButtonProps> = ({ onClick, text, isSubmit }) => {
  const buttonText = text ? text : 'Submit';
  /* If isSubmit is not passed then component will not break and will show Cancel button text.
  Not ideal and can be improved but at least component behaves sort of as expected. */
  return (
    <button onClick={onClick}>
      {isSubmit ? buttonText : 'Cancel'} 
    </button>
  )
};

3. Если все параметры являются необязательными, возможно, самое время спросить, в чем смысл / значение этого компонента / функции, если он может существовать без каких-либо свойств. Вы можете использовать свой компонент самостоятельно, не передавая никаких опор, чтобы посмотреть, как он выглядит и насколько он полезен.

4. Один из хороших случаев, когда необязательный параметр является работоспособным, - это когда он является частью конфигурации пользовательского интерфейса компонента (когда у нас есть некоторый внешний вид по умолчанию, который можно настроить с помощью свойств). Мы не хотим передавать isLarge как false все время, когда нам нужен наиболее распространенный вариант использования - маленькая кнопка:

OK code:
interface MyButtonProps {
  onClick: () => void;
  text: string;
  isSubmit: boolean;
  isLarge?: boolean;
}
const MyButton: React.FC<MyButtonProps> = ({ onClick, text, isSubmit, isLarge = false }) => {
  return (
    <button onClick={onClick} className={isLarge ? 'large-btn': 'small-btn'}>
      {isSubmit ? text : 'Cancel'}
    </button>
  )
};

Вывод:

В вашем заявлении у вас будут оба варианта необязательного «?» параметр и неопределенный тип, и это абсолютно нормально и зависит от конструкции вашего компонента. Вам просто нужно убедиться, что вы не злоупотребляете необязательными параметрами, чтобы сделать ваш компонент более «гибким», потому что:

  • это может увеличить время, затрачиваемое другими разработчиками на выяснение того, как должен работать компонент.
  • легко забыть удалить некоторые необязательные параметры в других файлах, если реализация функции была изменена, потому что Typescript не выделяет это.
  • люди склонны забывать писать защитный код для необязательного параметра, потому что они предполагают, что следующий разработчик выяснит, что должно пройти для правильной работы компонента (как в примере с кнопками выше: разработчик должен выяснить, что он должен передать «текст», если передаем значение флага).
  • Компонент с множеством необязательных параметров может быть трудно поддерживать и расширять без упущения некоторых деталей или потенциального варианта использования.