🦉 Проверка пропсов (Props Validation) 🦉

По мере того, как приложение становится сложным, может быть довольно небезопасно определять пропсы неформальным способом. Это приводит к двум проблемам:

  • трудно сказать, как следует использовать компонент, глядя на его код.
  • небезопасно, легко отправить неправильный пропрс в компонент при рефакторинге компонента, либо одного из его родителей.

Система типов пропсов решает обе проблемы, описывая типы и формы пропса. Вот как это работает в Owl:

  • ключ props является статическим ключом (поэтому отличается от this.props в экземпляре компонента)
  • это необязательно: для компонента нормально не определять ключ props.
  • пропсы проверяются всякий раз, когда компонент создается/обновляется
  • пропсы проверяются только в режиме dev (см. страницу конфигурации)
  • если ключ не соответствует описанию, выдается ошибка
  • он проверяет ключи, определенные в (статических) props. Дополнительные ключи, предоставленные родителем, вызовут ошибку.

For example:

class ComponentA extends owl.Component {
    static props = ['id', 'url'];

    ...
}

class ComponentB extends owl.Component {
  static props = {
    count: {type: Number},
    messages: {
      type: Array,
      element: {type: Object, shape: {id: Boolean, text: String }
    },
   date: Date,
   combinedVal: [Number, Boolean]
  };

  ...
}
  • это объект или список строк
  • список строк - это упрощенное определение пропсов, в котором перечислены только имена пропсов. Кроме того, если имя заканчивается на ?, оно считается необязательным.
  • все пропсы по умолчанию обязательны, если они не определены с помощью optional: true (в этом случае проверка выполняется только в том случае, если есть значение)
  • Допустимые типы: Number, String, Boolean, Object, Array, Date, Function и все функции-конструкторы (так что, если у вас есть класс Person, его можно использовать как тип)
  • массивы однородны (все элементы имеют одинаковый тип/форму)

Для каждого ключа определение prop является либо логическим значением, либо конструктором, либо списком конструкторов, либо объектом:

  • булево значение: указывает, что пропс существует и является обязательным.
  • конструктор: должен описывать тип, например: id: Number описываете пропс id как число
  • список конструкторов. В этом случае это означает, что мы допускаем более одного типа. Например, id: [Number, String] означает, что id может быть как строкой, так и числом.
  • объект. Это дает возможность иметь более выразительное определение. Затем разрешены (но не обязательны) следующие подключи:
    • type: основной тип проверяемого пропса
    • element: если тип был Array, то ключ element описывает тип каждого элемента в массиве. Если он не установлен, то мы проверяем только массив, а не его элементы,
    • shape: если тип был Object, то ключ shape описывает интерфейс объекта. Если он не установлен, то мы проверяем только объект, а не его элементы,
    • validate: это функция, которая должна возвращать логическое значение, чтобы определить, является ли значение действительным или нет. Полезно для пользовательской проверки.

Примеры:

  // задокументировано только существование этих 3 ключей
  static props = ['message', 'id', 'date'];
  // size не является обязательным
  static props = ['message', 'size?'];
  static props = {
    messageIds: {type: Array, element: Number},  // список чисел
    otherArr: {type: Array},   // просто массив. проверка подэлементов не производится
    otherArr2: Array,   // тож что и otherArr
    someObj: {type: Object},  // просто объект, без внутренней проверки
    someObj2: {
      type: Object,
      shape: {
        id: Number,
        name: {type: String, optional: true},
        url: String
      ]},    // объект, с ключами id (number), name (string, optional) и url (string)
    someFlag: Boolean,     // логическое, обязательное (даже если `false`)
    someVal: [Boolean, Date],   // либо логическое значение, либо дата
    otherValue: true,     // указывает, что это пропс
    kindofsmallnumber: {
      type: Number,
      validate: n => (0 <= n && n <= 10)
    },
    size: {
      validate:  e => ["small", "medium", "large"].includes(e)
    },
  };