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

Утверждение типа

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

Обычный вариант использования утверждения типа - это когда вы портируете код с JavaScript на TypeScript. Например, рассмотрим следующий шаблон:

var foo = {};
foo.bar = 123; // Ошибка: свойство 'bar' не существует в `{}`
foo.bas = 'hello'; // Ошибка: свойство 'bas' не существует в `{}`

Здесь ошибки в коде, потому что ожидаемый тип для foo это {}, т.е. объект без свойств. Поэтому вы не можете добавлять bar или bas к нему. Вы можете исправить это просто с помощью утверждения типа as Foo:

interface Foo {
    bar: number;
    bas: string;
}
var foo = {} as Foo;
foo.bar = 123;
foo.bas = 'hello';

as foo против <foo>

Первоначально был добавлен синтаксис <foo>. Это продемонстрировано ниже:

var foo: any;
var bar = <string>foo; // bar теперь имеет тип "string"

Однако существует двусмысленность в грамматике языка при использовании утверждений в стиле <foo> в JSX:

var foo = <string>bar;</string>;

Поэтому сейчас рекомендуется просто использовать as foo для единообразия.

Утверждение типа против приведения

Причина, по которой это не называется "приведением типа", заключается в том, что приведение обычно подразумевает некоторую поддержку среды выполнения. Тем не менее, утверждения типа - это просто конструкция для компиляции и способ для вас дать подсказки компилятору о том, как вы хотите, чтобы ваш код был проанализирован.

Утверждение считается опасным

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

interface Foo {
    bar: number;
    bas: string;
}
var foo = {} as Foo;
// аааа .... забыть что-нибудь?

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

interface Foo {
    bar: number;
    bas: string;
}
var foo = <Foo>{
    // компилятор предоставит автозаполнение для свойств Foo
    // Но разработчику легко забыть добавить все свойства
    // Также этот код может сломаться, если Foo подвергнется рефакторингу
    // (например, добавлено новое свойство)
};

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

interface Foo {
    bar: number;
    bas: string;
}
var foo: Foo = {
    // компилятор предоставит автозаполнение для свойств Foo
};

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

Двойное утверждение

Утверждение типа, несмотря на то, что, как мы показали, немного небезопасно, не является «чем-то абсолютно запрещённым». Например. следующее является очень даже допустимым случаем использования (например, пользователь думает, что переданное событие будет более конкретным случаем события), и утверждение типа работает как ожидалось:

function handler(event: Event) {
    let mouseEvent = event as MouseEvent;
}

Однако следующее, скорее всего, ошибка, и TypeScript будет жаловаться, как показано, несмотря на утверждение типа пользователем:

function handler(event: Event) {
    let element = event as HTMLElement; // Ошибка: Ни тип 'Event'
    // ни тип 'HTMLElement' не могут быть присвоены
}

Если вы все еще хотите этот тип, вы можете использовать двойное утверждение, а именно сначала сделайте утверждение any, которое совместимо со всеми типами, и поэтому компилятор больше не жалуется:

function handler(event: Event) {
    let element = (event as any) as HTMLElement; // Okay!
}

Как TypeScript определяет, что недостаточно одного утверждения

По сути, утверждение от типа S к T успешно выполняется, если либо S является подтипом T, либо T является подтипом S. Это делается для обеспечения дополнительной безопасности при выполнении утверждений типа ... совершенно безумные утверждения могут быть очень небезопасными, и вам нужно использовать any, чтобы можно было их использовать.