Изучение TypeScript — полное руководство для начинающих. Часть 5 — Строгий режим и сужение типов

от автора

Все привет! Меня зовут Лихопой Кирилл и я — fullstack-разработчик. В заключительной части руководства мы рассмотрим строгий режим и сужение типов в TypeScript. Эти техники позволяют точнее определять типы в коде, чтобы улучшить качество вашего кода и уменьшить количество багов.

Другие части: Часть 1 — Введение и примитивы Часть 2 — Ссылочные типы данных Часть 3 — Классы и интерфейсы Часть 4 — Литералы и дженерики

Строгий режим в TypeScript

Рекомендуется включить все проверки типов в файле tsconfig.json . После этого TypeScript будет выводить больше ошибок, однако эти ошибки помогут избежать множества багов в вашем приложении.

// tsconfig.json "strict": true

Давайте обсудим несколько моментов, связанных со строгим режимом:

никаких any и строгая проверка null.

Никаких неявных any

В примере ниже TypeScript делает вывод, что параметр a имеет тип any . Как вы можете заметить, когда мы передаем числе в эту функцию и пытаемся вывести свойство name ошибки не возникает. Это плохо.

function logName(a) { // Почему нет ошибок? console.log(a.name); }  logName(97);

С включенной опцией noImplicitAny TypeScript сразу же выдаст ошибку, если мы не укажем, какого типа должен быть a:

// ОШИБКА: Параметр 'a' неявно имеет тип 'any' function logName(a) { console.log(a.name); }

Строгая проверка null

Когда опция strictNullChecks отключена, TypeScript игнорирует null и undefined. Это может привести к неожиданным ошибкам в работе кода.

Если мы включим опцию strictNullChecks , null и undefined будут иметь собственный типы, и вы будете получать ошибку типа, если будете присваивать их переменным, которые ожидают определенный тип (например, string).

const getSong = () => {     return 'song'; };  let whoSangThis: string = getSong();  const singles = [     { song: 'bohemian rhapsody', artist: 'queen' },     { song: 'yellow submarine', artist: 'the beatles' }, ];  const single = singles.find(s => s.song === whoSangThis);  console.log(single.artist);

В примере выше нет гарантии, что singles.find найдет песню, однако сейчас код написан так, как будто такого случая не будет.

Если же мы включим опцию strictNullChecks , TypeScript будет выдавать ошибку, т.к. мы не гарантируем, что single существует.

const getSong = () => {     return 'song'; };  let whoSangThis: string = getSong();  const singles = [     { song: 'bohemian rhapsody', artist: 'queen' },     { song: 'yellow submarine', artist: 'the beatles' }, ];  const single = singles.find(s => s.song === whoSangThis);  console.log(single.artist); // ОШИБКА: возможно, объект 'undefined'.

По умолчанию, TypeScript говорит нам убедиться в том, что single существует, до его использования. То есть сначала надо проверить, что оно не является null или undefined:

if (single) { console.log(single.artist); // queen }

Сужение типов в TypeScript

В TypeScript переменная может перейти от менее точного типа к более точному. Этот процесс называется сужением типов.

Ниже вы можете посмотреть пример, который показывает, как TypeScript сужает менее точный тип string | number к более точному типу, когда мы используем условие с typeof:

function addAnother(val: string | number) { if (typeof val === 'string') { // TypeScript обрабатывает 'val' как строку в этом блоке,  //поэтому мы можем использовать строковые методы, и TypeScript не выдаст ошибку return val.concat(' ' + val); }        // Здесь TypeScript уже знает, что 'val' - это число     return val + val; }  console.log(addAnother('Супер')); // Супер Супер console.log(addAnother(20)); // 40

Другой пример: у нас объявлен объединенный тип  PlaneOrTrain, который может быть типа Plane или Train.

interface Vehicle { topSpeed: number; } interface Train extends Vehicle {     carriages: number; }  interface Plane extends Vehicle {     wingSpan: number; }  type PlaneOrTrain = Plain | Train;  function getSpeedRatio(v: PlaneOrTrain) {     // Здесь мы хотим вернуть отношение topSpeed/carriages или topSpeed/wingSpan     console.log(v.carriages); // ОШИБКА: 'carriages' не существует для типа 'Plane' }

С тех пор, как getSpeedRatio работает с несколькими типами, нам нужен способ различать, является ли v типом Plane или Train. Мы можем сделать это, добавив для каждого типа отличительное свойство с литеральным строковым типом.

// У всех объектов типа Train теперь будет свойство со значением 'Train' interface Train extends Vehicle { type: 'Train'; carriages: number; }  // У всех объектов типа Plane теперь будет свойство со значением 'Plane' interface Plane extends Vehicle {     type: 'Plane';     wingSpan: number; }  type PlaneOrTrain = Plain | Train;

Теперь мы можем использовать сужение типов TypeScript для v:

function getSpeedRatio(v: PlaneOrTrain) { if (v.type === 'Train') { //  Теперь TypeScript знает, что 'v' типа 'Train', поэтому сработало сужение и ошибки нет return v.topSpeed / v.carriages; }        // Если 'v' не типа 'Train', то сужение опять срабатывает и TypeScript знает, что здесь у 'v' тип 'Plane'     return v.topSpeed / v.wingSpan; }  let bigTrain: Train = {     type: 'Train',     topSpeed: 100,     carriages: 20, };  console.log(getSpeedRatio(bigTrain)); // 5

На этом руководство подошло к концу. Буду рад критике и отзывам в комментариях 🙂


ссылка на оригинал статьи https://habr.com/ru/post/709580/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *