Съдържание на курса

Урок 0 — Какво е TypeScript и какво НЕ е

От Dorian Chávez · основател на Hábil и архитект на интеграции ·

Време: 90 мин (или 2 × 45)

Какво изграждаш: засега нищо (четене)

Какво научаваш: JS срещу TS; типовете се изтриват при изпълнение; какво защитават и какво не; защо strict

След урока ще можеш да

  • Обясниш разликата между JavaScript и TypeScript, без да казваш, че са два езика, които се състезават в браузъра.
  • Компилираш файл .ts, изпълниш генерирания JavaScript и разпознаеш каква информация за типовете е изчезнала.
  • Откриеш грешка, която TypeScript може да спре преди изпълнението на програмата.
  • Откриеш случай, в който тип, записан в TypeScript, не стига, за да защити данни, идващи отвън.
  • Обясниш защо курсът използва strict от самото начало.
  • Четеш и поправяш грешките TS2345 и TS18048 на компилатора.

Защо, преди как

revisor, който ще изграждаш в този курс, пита няколко услуги, събира отговорите им и показва отчет. Макар в началото да изглежда като малка програма, той съдържа проблем, който се повтаря почти във всяка система: има данни с очаквана форма и има данни, които могат да пристигнат с грешна форма. Една услуга трябва да има име, URL и състояние; един отговор трябва да включва HTTP код; таблото трябва да получи същия отчет, който е произвела API. Ако някой обърка URL с числов код, сгреши името на свойство или третира като успешен отговор един непълен обект, програмата може да се счупи късно: може би едва когато някой отвори таблото или когато външна услуга отговори по необичаен начин.

JavaScript позволява да се пишат полезни програми с много малко прегради. Можеш да създадеш обект, да му добавиш свойство по-късно, да подадеш низ там, където друга функция е очаквала число, и да изпълниш файла веднага. Тази гъвкавост е една от добродетелите му: JavaScript е подходящ за експерименти, автоматизиране на задачи, изграждане на интерфейси и бързи промени. Цената се появява, когато програмата расте, когато няколко души работят по един и същ код или когато една функция престава да е очевидна сама по себе си. Редакторът вече не може да знае със сигурност какви данни получава функцията, а ти завършваш с важни правила, пазени в паметта, в коментари или в надеждата, че тестовете покриват всички пътища.

TypeScript добавя слой за проверка преди изпълнението. Този слой описва какви стойности може да получи една функция, какви свойства трябва да има един обект и какви резултати може да даде една операция. С тази информация компилаторът проверява дали частите на програмата си пасват. Той не чака потребителят да срещне счупен екран, нито истинска заявка да стигне до продукция: отбелязва много грешки, докато пишеш или компилираш.

Важната дума е „много“, а не „всички“. TypeScript не заменя тестовете, не превръща външните данни в надеждни данни и сам по себе си не пречи на една функция да има погрешно бизнес правило. Ако revisor смята, че HTTP отговор 500 означава „услугата е налична“, TypeScript може да провери, че кодът е число, но не може да отгатне, че твоят оперативен критерий е неправилен. Типовете описват структура и връзки между стойности; те не познават автоматично света, който тези стойности представляват.

Удобно е още от първия урок да се премахне и едно често объркване: TypeScript не замества JavaScript по време на изпълнение. Node, браузърът и React изпълняват JavaScript. Обичайният поток в курса е да пишеш TypeScript, да го проверяваш с tsc и да превръщаш резултата в JavaScript, преди да го изпълниш. Когато revisor вече работи, типовете му Servicio, Estado и Reporte няма да ги има като обекти, които Node може да разгледа. Това има важни последици: една анотация може да предотврати грешка в собствения ти код, но не валидира JSON, който пристига по HTTP, нито променя стойност, която вече е неправилна.

Node 24 LTS може също да изпълнява директно скрипт .ts, чийто синтаксис е изтриваем. В такъв случай той заменя типовете с интервали и изпълнява останалия JavaScript: не проверява типове, не чете tsconfig.json и не допуска синтаксис, който генерира код, като enum. Използвай го, ако ти е удобно, за отделен скрипт; revisor ще има няколко файла и ще се компилира с tsc, за да се изпълнява dist/*.js. Страницата на Node за TypeScript описва това премахване на типовете (type stripping) и ограниченията му.

В Go компилаторът също проверява типовете, преди да създаде изпълнимия файл. Практическата разлика е, че програмата на Go се превръща в нативен бинарен файл, докато TypeScript произвежда JavaScript за платформа, която вече съществува: Node или браузъра. Полезното сравнение не е да решим кой „е по-строг“, а да разпознаем обща дисциплина: да декларираме договори, за да излизат грешките при интеграция по-рано. В Go тези договори се пишат с типовете на езика; в TypeScript се пишат върху JavaScript и се премахват преди изпълнението.

Затова курсът започва с урок за четене. Преди да научиш синтаксиса, трябва да знаеш какво обещава инструментът и какво не обещава. Ако очакваш TypeScript автоматично да валидира конфигурационен файл, ще останеш с фалшиво чувство за сигурност. Ако мислиш, че само добавя досадни анотации, вероятно ще изключиш проверките тъкмо когато могат най-много да ти помогнат. Целта е да го използваш като това, което е: статичен проверител, който прави видими договорите на програмата ти и те принуждава да обърнеш внимание на местата, където тези договори още не стигат.

Понятията

JavaScript остава програмата, която се изпълнява

JavaScript е динамичен език. Това означава, че стойностите му се разглеждат, докато програмата работи. Една променлива може да съдържа низ сега и, ако я присвоиш наново, да съдържа число по-късно. Една функция може да получи каквато и да е стойност, освен ако сам не напишеш проверки по време на изпълнение. JavaScript не изисква да се декларират всички типове, защото моделът му е замислен така, че много неща да се решават в момента на изпълнение.

Това не значи, че JavaScript е немарлив, нито че програма на JavaScript е обречена да се чупи. Можеш да пишеш много ясен JavaScript, да го тестваш добре и да валидираш всеки вход. Проблемът е в мащаба и в обратната връзка. Ако mostrarEstado трябва да получава едно от две възможни състояния, JavaScript не те предупреждава, когато напишеш "disponble" с липсваща буква. Програмата може да продължи да работи и да покаже грешен етикет или да мине по клон, който никой не е очаквал. Грешката остава скрита, докато някой конкретен път не я разкрие.

TypeScript взима същия JavaScript код и позволява да се опишат границите му. Литерален тип като "disponible" | "falla" изразява, че не върши работа какъв да е низ: вършат работа само тези два. Когато една функция приема този тип, TypeScript сравнява всяко извикване с договора, преди да издаде JavaScript. Той не изчислява истинското състояние на една услуга; проверява само дали частите на програмата ти използват един и същ речник.

// fig00_01.ts
type Estado = "disponible" | "falla";

function describir(estado: Estado): string {
  return estado === "disponible" ? "Servicio disponible" : "Servicio con falla";
}

console.log(describir("disponible"));
$ npx tsc --strict --target ES2022 --module nodenext fig00_01.ts
$ node fig00_01.js
Servicio disponible

Програмата изглежда почти като JavaScript. Специфичните за TypeScript части са type Estado, обединението от литерали и анотациите : Estado и : string. Останалото —функцията, тернарният оператор и console.log— е обикновен JavaScript. Тази приемственост е предимство за този, който вече програмира на JavaScript: не започваш от нула и не учиш друга машина за изпълнение; добавяш информация, която компилаторът и редакторът могат да проверяват.

Стойността на тази информация нараства, когато типът се използва повторно. Ако всяка функция на revisor измисляше свои низове за описание на състоянието, скоро щеше да имаш "ok", "OK", "disponible" и "funcionando" за една и съща идея. Всички са валидни низове за JavaScript, но не всички са валидни за отчета, който искаш да изградиш. Споделеният тип установява малък език за проекта. По-нататък този език ще включва състояния с подробности, продължителност и HTTP код.

В revisor същата идея се появява още от най-малкия възможен модел. Още не става дума да питаме URL, нито да отваряме сървър: става дума да не позволим на функциите, които обработват резултатите, да говорят различни диалекти. Ако Estado казва, че резултатите могат да бъдат "disponible" или "falla", функция, която получава услуга, може да се опре на това решение, а екранът може да покаже двете алтернативи, които наистина съществуват.

// fig00_02.ts
type Estado = "disponible" | "falla";

type Servicio = {
  nombre: string;
  estado: Estado;
};

function resumen(servicio: Servicio): string {
  return `[${servicio.estado}] ${servicio.nombre}`;
}

const catalogo: Servicio = {
  nombre: "catálogo",
  estado: "disponible",
};

console.log(resumen(catalogo));
$ npx tsc --strict --target ES2022 --module nodenext fig00_02.ts
$ node fig00_02.js
[disponible] catálogo

Тук TypeScript проверява няколко връзки наведнъж. catalogo трябва да има nombre и estado; nombre трябва да е низ; estado трябва да е един от двата разрешени литерала; а resumen приема само обект с тази форма. Компилаторът няма нужда да изпълнява мрежова заявка, за да открие противоречие: вижда го, като сравнява написаната стойност с договора Servicio.

Не е нужно да анотираш всяка променлива. TypeScript може да извежда много типове от стойностите. Например, ако напишеш const nombre = "catálogo", компилаторът знае, че това е текст. Анотациите са по-ценни по краищата на една функция, в данни, които се споделят между модули, и в решения, които искаш да превърнеш в договор. Да напишеш const nombre: string = "catálogo" не добавя полезна информация; да напишеш function resumen(servicio: Servicio): string вече съобщава какво влиза и какво излиза.

Изведането на типове също не отменя нуждата да мислиш. Компилаторът извежда от наличния код, а не от намерението, което си забравил да изразиш. Ако един списък може да съдържа налични и неуспешни услуги, ще трябва да моделираш тази разлика изрично. Ако една стойност може да липсва, ще трябва да го допуснеш в типа и да се справиш със случая. TypeScript намалява механичната работа по повтаряне на очевидни типове, за да съсредоточиш вниманието си върху договорите, които променят поведението на програмата.

Типовете се изтриват преди изпълнението

Анотацията на тип не е инструкция за Node. Когато компилираш fig00_02.ts, генерираният файл запазва функцията, обекта и извикването на console.log, но премахва type Estado, type Servicio, : Estado, : Servicio и : string. Node няма нужда да ги разбира, защото никога не ги получава.

Същественият JavaScript, който се получава от този пример, изглежда така:

function resumen(servicio) {
  return `[${servicio.estado}] ${servicio.nombre}`;
}

const catalogo = {
  nombre: "catálogo",
  estado: "disponible",
};

console.log(resumen(catalogo));

Този фрагмент не съдържа дефиниция на Servicio. Не съдържа и списък с валидни стойности за Estado. Проверката е станала по време на компилацията, преди Node да прочете файла. Затова се казва, че типовете на TypeScript се изтриват: те са информация за проверка и разработка на програмата, а не данни, които автоматично придружават програмата, докато работи.

Това решение има предимства. Издаденият JavaScript не се нуждае от библиотека за рефлексия само за да запази анотациите. Браузърът не изтегля представяне на всеки тип само защото проектът използва TypeScript. Node може да изпълни резултата така, както изпълнява всеки друг JavaScript модул. Освен това можеш да възприемеш TypeScript постепенно: много валиден JavaScript код може да съжителства с файлове .ts, докато добавяш договори там, където са нужни.

Има и последица, която трябва да повтаряш, докато стане интуитивна: да напишеш тип не превръща стойност. Ако твърдиш, че една променлива е number, компилаторът проверява операциите в TypeScript кода, но издаденият JavaScript не превръща "404" в 404. Ако декларираш, че едно свойство съществува, Node не създава това свойство. Ако получен JSON не съдържа url, никоя анотация няма да го накара да се появи. Анотациите описват очакване; те не изфабрикуват и не поправят данни.

Това отличава типовете от валидацията. Валидацията се изпълнява и решава какво да се прави с реална стойност: да я отхвърли, да я поправи, да я преобразува или да върне грешка. Статичният тип позволява на компилатора да разсъждава за стойности, които програмата вече смята за надеждни. И двете неща са необходими, но се случват на различни места. В този курс типовете на модела ще се появят първи; валидацията на JSON, променливи на средата и HTTP отговори ще дойде в урок 6, когато вече ще ти е ясно защо не може да е автоматична.

revisor ще има типове, споделени между API и таблото. Това позволява двете части да са съгласни за формата на един отчет, докато се разработват. Но когато браузърът получи JSON от API, той получава JSON: текст, превърнат в JavaScript обекти, а не вълшебен екземпляр на типа Reporte. API трябва да построи правилен отговор, а таблото трябва да третира мрежовата граница внимателно. Споделянето на типове предотвратява много противоречия вътре в хранилището; не отменя нуждата да се валидира границата.

Изтриването обяснява и защо не можеш да попиташ нещо като if (servicio is Servicio), използвайки type на TypeScript. Името Servicio вече не съществува, когато Node работи. Можеш обаче да проверяваш конкретни свойства с JavaScript, например да провериш, че една стойност е обект, че има свойство nombre от тип низ и че URL адресът ѝ също е низ. Тази проверка ще бъде част от функция за валидация, а не част от дефиницията на типа.

Практическото правило е просто: използвай типове, за да изразиш договори между кода, който контролираш; използвай валидация, за да решиш дали приемаш данни, които идват отвън. В реалния живот има сиви зони, като данни от външна библиотека или файлове, създадени от друга част на същата система. Ако не можеш да докажеш, че един вход изпълнява договора, третирай го като ненадежден, докато не го валидираш.

TypeScript защитава вътрешните договори, не външната реалност

Компилаторът вижда само кода, който получава, и типовете, налични за анализа му. Може да открие, че си подал число на функция, която иска низ. Може да открие, че се опитваш да използваш несъществуващо свойство на обект, чийто тип познава. Може да открие, че една променлива може би е undefined. Не може да отвори HTTP връзка, да провери дали доставчикът е спазил документацията си, нито да разбере дали конфигурацията, която потребител е написал вчера, все още има правилния формат днес.

Най-опасният случай за начинаещите е твърдението за тип с as. Израз като valor as Servicio не валидира стойността. Той казва на компилатора: „оттук нататък се довери, че знам, че това е Servicio“. Понякога е разумно, когато вече си направил проверка, която TypeScript не е успял да изведе. Но да го използваш, за да заглушиш съмнение за външни данни, е като да свалиш предпазния колан, защото алармата звъни.

За да изолираме тази граница, Servicio от следващата фигура използва съкратена форма, различна от Servicio със estado от fig00_02.ts: сега запазва само nombre и url. Пълният и стабилен модел на revisor ще дойде в урок 3.

// fig00_03.ts
type Servicio = {
  nombre: string;
  url: string;
};

const servicio = JSON.parse(
  '{"nombre":"pagos","direccion":"https://pagos.example"}',
) as Servicio;

console.log(`${servicio.nombre}: ${servicio.url}`);
$ npx tsc --strict --target ES2022 --module nodenext fig00_03.ts
$ node fig00_03.js
pagos: undefined

Файлът се компилира без грешка, защото твърдението е принудило компилатора да третира резултата като Servicio. Обаче JSON има direccion, а не url. При изпълнение JavaScript търси несъществуващо свойство и получава undefined. За времето за изпълнение няма противоречие: JavaScript обектите могат да нямат свойство. Противоречието е между обещанието, записано с as Servicio, и реалните данни.

Този пример не означава, че JSON.parse е лош, нито че TypeScript е безполезен спрямо JSON. Означава, че редът е важен. Първо получаваш стойност, чиято форма не познаваш; после проверяваш свойствата ѝ; и едва тогава я превръщаш в стойност, която останалата част от програмата може да използва като Servicio. TypeScript представя тази изходна точка с unknown, тип, който те принуждава да огледаш, преди да достъпиш свойства. Ще го изучиш по-подробно, когато revisor чете конфигурацията си и обработва отдалечени отговори.

Има и други граници, които трябва да разпознаваш. TypeScript не знае дали един URL сочи към истински сървър. Не знае дали състоянието "disponible" описва правилно здравето на една услуга. Не знае дали две заявки, пристигнали едновременно, променят даден ресурс по несъвместим начин. Не знае дали парола е била изложена в журнал. Може да ти помогне да моделираш данните така, че тези проблеми да се виждат и тестват по-лесно, но решенията за сигурност, съвместно изпълнение и бизнес изискват дизайн, валидация и тестове.

Типът също може да е лошо проектиран. Ако декларираш, че codigoHttp е number, от гледна точка на типа ще приемеш -5, 999 и 3.14. Може би на програмата ѝ трябва само да знае, че е число; може би домейнът изисква цяло число между 100 и 599. Второто правило не следва само от number. По-нататък ще решаваш къде да представиш ограниченията на домейна: с обединения от литерали, валидатори, конструиращи функции или комбинация от тях.

В revisor данните, които построяват твоите собствени функции, са зона, в която TypeScript защитава много. Ако crearReporte получава вече проверени услуги и връща позната структура, типовете не позволяват на API и таблото да се разминават в имената на свойствата. Отговорът, който идва от URL, конфигуриран от човек, е друга зона: споделеният тип не доказва, че сървърът е върнал обещания JSON. Тази граница се валидира, преди данните да се превърнат във вътрешни резултати.

Здравословен начин да мислиш за системата е да начертаеш линия. От вътрешната страна остави strict да бъде взискателен и избягвай да се измъкваш с any или с твърдения без доказателство. По границите приеми, че стойността още не заслужава доверие, и я валидирай. Типът не изчезва като дисциплина, защото външните данни са несигурни; напротив, помага ти да посочиш точно момента, в който те преминават от несигурни към използваеми.

strict превръща честите съмнения в явна работа

TypeScript има опции за компилация, които определят колко проверява. От TypeScript 6 strict е true по подразбиране; TypeScript 7.0.2 вече тръгва от тези проверки. Командите в този урок пишат --strict, за да е явно решението на курса, а не защото компилаторът има нужда от това, за да го активира. Който използва --strict false, изключва тези проверки умишлено. Тази гъвкавост е полезна за внимателно ограничена миграция, но не е най-добрата отправна точка за нов проект.

Опцията strict активира набор от строги проверки. Сред най-видимите са noImplicitAny, която не позволява стойности без тип тихомълком да станат any, и strictNullChecks, която различава налична стойност от такава, която може да е null или undefined. Наборът може да расте в бъдещи версии на TypeScript; затова е по-добре да активираш общата опция, отколкото да помниш списък от изолирани флагове.

В курс от нулата strict не е наказание, нито начин да се пише повече текст. Това е решение да откриваш рано местата, където програмата ти не е изразила нещо важно. Ако една функция приема подробност, която може да липсва, това отсъствие е част от договора ѝ. Ако един параметър няма тип, може би си забравил да решиш какъв вид стойности поддържа. Ако компилаторът те принуждава да го решиш, той предотвратява друг човек да трябва да го гадае после.

// fig00_04.ts
function etiquetaDetalle(detalle: string | undefined): string {
  if (detalle === undefined) {
    return "sin detalle";
  }

  return detalle.toUpperCase();
}

console.log(etiquetaDetalle(undefined));
console.log(etiquetaDetalle("200 OK"));
$ npx tsc --strict --target ES2022 --module nodenext fig00_04.ts
$ node fig00_04.js
sin detalle
200 OK

Функцията не се преструва, че detalle винаги съществува. Декларира string | undefined, проверява случая на отсъствие и вика toUpperCase() само когато TypeScript може да докаже, че е останал низ. Това намаляване на възможностите се нарича стесняване на типа (narrowing): след условието типът е по-конкретен. Не е нужно да помниш термина днес; нужно е да придобиеш навика да обръщаш внимание на случая, който договорът казва, че може да настъпи.

В revisor подробностите за една грешка могат да липсват. Една услуга може да отговори с HTTP код без допълнителен текст; една връзка може да приключи, преди да даде отговор; една конфигурация може да пропусне незадължителен етикет. Ако моделираш всичко като string, програмата ти позволява да го използваш, сякаш винаги има съдържание. С strictNullChecks типът запазва разликата между „има текст, макар и празен“ и „не е получен никакъв текст“. Тази разлика подобрява както съобщенията на таблото, така и логиката на диагностиката.

strict не обещава, че никога няма да напишеш твърдение за тип, нито че всички случаи ще са очевидни. Ще има интеграции с библиотеки, API на браузъра или външни данни, където ще трябва да направиш конкретна проверка. Разликата е, че изходът ще бъде умишлен и локализиран. Без strict съмненията се разпространяват: един any влиза през една функция, минава през още пет и накрая всеки достъп до свойство изглежда валиден. Тогава намирането на източника струва много повече.

Има хора, които включват строгите проверки накрая, когато проектът вече има хиляди редове. Това обикновено превръща възприемането в тежко почистване: появяват се много висящи решения наведнъж, а натискът да се предаде работата води до изключване на правила или до пълнене на кода с as any. Ако започнеш в строг режим, цената остава малка. Всяка нова функция решава договорите си, когато се ражда, а всеки нов тип остава достъпен за функциите, които идват след него.

Go преподава сходен урок: компилаторът не ти позволява да пренебрегнеш много несъвместимости, които други езици откриват късно. TypeScript запазва гъвкавостта на JavaScript, защото може да се възприема малко по малко, но този курс ще избере най-взискателния път за новия код. Намерението не е компилаторът да „печели“ спор, а да превърне реалните двусмислия във видими решения.

Когато в урок 1 създадеш tsconfig.json, strict ще бъде част от основната конфигурация. Оттам нататък грешка в типовете не се поправя с махане на опцията. Поправя се чрез изясняване на договора: анотиране на вход, проверка на стойност, която може да липсва, разделяне на различни състояния или валидиране на външни данни. Тази практика ще бъде една от основите на revisor.

Грешката, която ще видиш

Първата грешка се появява, когато едно извикване противоречи на типа, който функцията е декларирала. Следващата програма иска URL като низ, но получава число. tsc 7.0.2 няма нужда да изпълнява файла, за да открие проблема.

// fig00_05.ts
function consultarServicio(url: string): void {
  console.log(url);
}

consultarServicio(404);
$ npx tsc --strict --target ES2022 --module nodenext fig00_05.ts
fig00_05.ts(6,19): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

TS2345 означава, че стойността на един аргумент не може да се присвои на типа на съответния параметър. Числото 404 може да е HTTP код, но не е URL. Поправката не е да превърнеш каквито и да са данни в низ, за да заглушиш грешката. Първо реши какво представлява функцията: ако пита адрес, получава низ като "https://pagos.example"; ако обработва HTTP код, създай друга функция, чийто параметър е число. Грешката е показала, че две различни понятия са били смесени.

Втората грешка е пряка последица от strictNullChecks. Типът приема низ или undefined, но програмата се опитва да го използва, сякаш винаги е низ.

// fig00_06.ts
function etiquetaDetalle(detalle: string | undefined): string {
  return detalle.toUpperCase();
}

console.log(etiquetaDetalle(undefined));
$ npx tsc --strict --target ES2022 --module nodenext fig00_06.ts
fig00_06.ts(3,10): error TS18048: 'detalle' is possibly 'undefined'.

TS18048 означава, че компилаторът е намерил валиден път, в който detalle няма стойност. Ако този JavaScript се изпълни с undefined, опитът да се прочете toUpperCase би предизвикал грешка при изпълнение. Поправката е да се обработи отсъствието, преди да се използва низът, както е направил fig00_04.ts, или да се промени договорът, така че функцията да получава само string, когато тази гаранция наистина съществува.

Съобщенията на TypeScript са подсказки, а не механични инструкции. Една грешка може да се реши с условие, с по-добре проектиран тип, с валидация или с друга функция. Полезният въпрос не е „как да накарам TS18048 да изчезне?“, а „може ли тези данни да липсват според правилата на програмата?“. Ако отговорът е да, обработи случая. Ако отговорът е не, намери къде липсва валидацията, която трябва да го гарантира.

Помни също, че по конфигурация TypeScript може да издаде JavaScript дори когато е съобщил за грешка. Компилаторът е замислен така, че постепенната миграция да не спира веднага съществуващ JavaScript проект. В revisor грешките при компилация ще се третират като повреди, които трябва да се поправят, преди една промяна да се смята за завършена. Урок 1 ще настрои проекта така, че тази политика да е явна.

Какво се прави погрешно

  • Да се използва any, за да се махне грешка. any изключва много проверки точно върху стойността, за която ти е трябвала най-много информация. Понякога се появява при интегриране на съществуващ код, но не трябва да е автоматичният изход. Предпочитай unknown, когато стойността идва отвън, и стеснявай типа ѝ постепенно чрез валидации.

  • Да се пише as Servicio върху данни от JSON, HTTP или променливи на средата. Твърдението не оглежда стойността; само променя това, което TypeScript предполага за нея. Ако няма предварителна валидация, можеш да получиш същия undefined като в fig00_03.ts, с подвеждащ вид на сигурност.

  • Да се вярва, че TypeScript замества тестовете. Типовете откриват структурни несъвместимости, но не доказват, че една заявка стига до правилния сървър, че таймаутът работи или че един отчет подрежда услугите така, както е поискал потребителят. Използвай типове, за да затвориш един клас грешки, и тестове, за да наблюдаваш реалното поведение.

  • Да се изключва strict, когато се появят няколко съобщения. Грешките обикновено разкриват висящо решение: параметър без договор, незадължителни данни, третиран като задължителен, или външна граница без валидация. Изключването на правилото скрива работата, но не премахва двусмислието на програмата.

  • Да се анотира абсолютно всичко. TypeScript извежда прости типове точно. Повтарянето на const nombre: string = "pagos" добавя шум, без да засилва някаква граница. Запази анотациите за публичните договори, параметрите, значимите резултати и споделените модели като Servicio.

  • Да се бърка типът с бизнес правило. codigoHttp: number не гарантира, че едно число съответства на валиден HTTP отговор. Типовете изразяват част от домейна; останалите правила се нуждаят от валидация, тестове и явни решения.

Упражнения

Упражнение 1 — Разделяне на понятията

Прочети извикването consultarServicio(404) от fig00_05.ts. Напиши две изречения: едно, което обяснява защо TS2345 е прав, и друго, което предлага правилна стойност за функция, която получава URL. После напиши втора сигнатура на функция, подходяща за обработка на числов HTTP код.

Упражнение 2 — Откриване на фалшиво обещание

Тръгни от JSON на fig00_03.ts. Без да изпълняваш програмата, определи свойството, което не съвпада със Servicio, и предскажи точния изход на console.log. Обясни защо as Servicio е позволило компилацията, въпреки че обектът няма очакваната форма.

Упражнение 3 — Да направим отсъствието явно

Промени наум fig00_06.ts така, че да връща "sin detalle", когато получи undefined, и да превръща в главни букви наличния низ. Напиши какъв трябва да е изходът за undefined и за "tiempo agotado". После го сравни с решението.

Упражнение 4 — От типа до границата на системата

revisor чете списък от услуги от външен източник. Обясни къде би поставил всяка отговорност: типа Servicio, валидацията, че nombre и url са низове, и проверката, че URL отговаря. Обоснови защо нито една от трите не замества другите две.

Решения

Решение 1

TS2345 е прав, защото 404 е число, а функцията е декларирала, че ѝ трябва низ на име url. Правилна стойност за тази функция би могла да бъде "https://pagos.example". Ако намерението е било да се работи с кода, подходяща сигнатура би била function describirCodigoHttp(codigo: number): string. Разделянето на функциите не позволява един и същ параметър да представя две различни идеи.

Решение 2

Неправилното свойство е direccion; типът Servicio очаква url. Изходът е pagos: undefined. Твърдението as Servicio не е сравнило обекта с типа, нито е добавило липсващото свойство; то е казало на компилатора да се довери на твърдение, което програмата не е проверила. Времето за изпълнение вижда само един JavaScript обект с nombre и direccion.

Решение 3

Функцията трябва да провери случая на отсъствие, преди да извика toUpperCase(). За undefined изходът трябва да е sin detalle. За "tiempo agotado" изходът трябва да е TIEMPO AGOTADO. Пълното решение следва същия модел като fig00_04.ts: едно условие разрешава отсъствието и след него TypeScript знае, че оставащата стойност е низ.

Решение 4

Типът Servicio принадлежи на вътрешния код, споделен между частите на revisor: изразява, че една използваема услуга има nombre и url от тип низ. Валидацията принадлежи точно там, където списъкът влиза в системата: получава стойност, която още е несигурна, проверява свойствата ѝ и отхвърля или съобщава за невалидни данни. Проверката дали URL отговаря принадлежи на мрежовата операция, защото низ с форма на URL може да сочи към несъществуващ, бавен сървър или към такъв с неуспешен отговор. Типът подрежда кода; валидацията защитава границата; запитването наблюдава реалното състояние на услугата.

Как разбирам, че съм успял

Можеш да считаш този урок за завършен, когато изпълняваш тези проверки:

  • Можеш да изпълниш npx tsc --version и да получиш Version 7.0.2.

  • Можеш да изпълниш node --version и да получиш версия, която започва с v24.

  • Като копираш fig00_01.ts, го компилираш с npx tsc --strict --target ES2022 --module nodenext fig00_01.ts и изпълниш node fig00_01.js, получаваш точно Servicio disponible.

  • Като компилираш fig00_05.ts със същата команда, получаваш TS2345 и не се опитваш да го изпълниш, сякаш е правилна програма.

  • Можеш да обясниш защо fig00_03.ts отпечатва undefined, макар да се компилира без грешки.

  • Можеш да поправиш fig00_06.ts, без да махаш strict и без да променяш типа, за да се преструваш, че undefined никога не може да стигне дотам.

  • Можеш да кажеш, без да гледаш този урок, че TypeScript проверява преди изпълнението, издава JavaScript и сам по себе си не валидира външни данни.

За допълнително четене

  • TypeScript Handbook: The Basics — официална документация на TypeScript; консултирано на 2 октомври 2026 г.

  • TSConfig: strict — официална документация на опцията strict; консултирано на 2 октомври 2026 г.

  • Node.js: TypeScript — официална документация на Node за изпълнение и поддръжка, свързани с TypeScript; консултирано на 2 октомври 2026 г.

  • MDN: TypeScript — определение и контекст на TypeScript в MDN; консултирано на 2 октомври 2026 г.

Да обсъдим вашия проект

Предпочитате имейл? Пишете ни на hola@habil.mx