
Все привет! Меня зовут Лихопой Кирилл и я - fullstack-разработчик. В заключительной части руководства мы рассмотрим строгий режим и сужение типов в TypeScript. Эти техники позволяют точнее определять типы в коде, чтобы улучшить качество вашего кода и уменьшить количество багов.
Другие части:
Строгий режим в 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
На этом руководство подошло к концу. Буду рад критике и отзывам в комментариях :)
