Перейти к содержанию

Freshness

TypeScript предоставляет концепцию Freshness (также называемую строгой проверкой литеральных объектов), чтобы упростить проверку типов литеральных объектов, которые без этой особой проверки были бы структурно совместимы.

Структурная типизация крайне удобна. Рассмотрим следующий фрагмент кода. Это позволяет вам очень удобно обновить ваш JavaScript до TypeScript, предохраняя типы на определённом уровне:

function logName(something: { name: string }) {
    console.log(something.name);
}

var person = { name: 'мэтт', job: 'быть потрясающим' };
var animal = {
    name: 'корова',
    diet: 'веган, но употребляет молоко собственного вида',
};
var random = { note: `У меня нет свойства name` };

logName(person); // okay
logName(animal); // okay
logName(random); // Ошибка: свойство `name` не найдено

Тем не менее, структурная типизация имеет недостаток: она вводит в заблуждение мыслью, что что-то способно принимает больше данных, чем на самом деле. Это показано в следующем коде, на котором TypeScript будет выдавать ошибку:

function logName(something: { name: string }) {
    console.log(something.name);
}

logName({ name: 'мэтт' }); // okay
logName({ name: 'мэтт', job: 'быть потрясающим' });
// Ошибка: литералы объекта должны указывать только известные свойства.
// `job` здесь лишнее.

Обратите внимание, что эта ошибка возникает только для литералов объекта. До этой ошибки можно посмотреть на вызов logName({ name: 'мэтт', job: 'быть потрясающим' }) и подумать, что logName выполнится с job, когда на деле жестко отклоняет его.

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

function logIfHasName(something: { name?: string }) {
    if (something.name) {
        console.log(something.name);
    }
}
var person = { name: 'мэтт', job: 'быть потрясающим' };
var animal = {
    name: 'корова',
    diet: 'веган, но употребляет молоко собственного вида',
};

logIfHasName(person); // okay
logIfHasName(animal); // okay
logIfHasName({
    neme: 'Я просто неправильно написал name как neme',
}); // Ошибка: литералы объекта должны указывать только известные свойства.
// Здесь `neme` лишнее.

Причина, по которой только объектные литералы проверяются по типу таким образом, заключается в том, что в этом случае дополнительные свойства, которые на самом деле не используются, почти всегда являются опечаткой или неправильным пониманием API.

Разрешение дополнительных свойств

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

var x: { foo: number; [x: string]: any };
x = { foo: 1, baz: 2 }; // Ok, `baz` соответствует сигнатуре индекса

Пример использования: React State

Facebook ReactJS предлагает хороший вариант использования для freshness объекта. Довольно часто в компоненте вы вызываете setState с несколькими свойствами вместо того, чтобы передавать все свойства, т.е.

// Допустим
interface State {
  foo: string;
  bar: string;
}

// Вы хотите сделать:
this.setState({foo: "Hello"}); // Ошибка: отсутствует свойство bar

// Но так как state содержит и `foo` и `bar` TypeScript заставит вас
// сделать это:
this.setState({foo: "Hello", bar: this.state.bar}};

Используя идею freshness, вы пометите все элементы как необязательные и вы все равно сможете отловить опечатки!:

// Допустим
interface State {
  foo?: string;
  bar?: string;
}

// Вы хотите сделать:
this.setState({foo: "Hello"}); // Ура работает отлично!

// Благодаря freshness также защищено от опечаток!
this.setState({foos: "Hello"}}; // Ошибка: объекты могут указывать только
// известные свойства

// И тип тоже проверяется
this.setState({foo: 123}}; // Ошибка: невозможно присвоить номер строке