diff --git a/README-be_BY.md b/README-be_BY.md new file mode 100644 index 00000000..4a28fd41 --- /dev/null +++ b/README-be_BY.md @@ -0,0 +1,5034 @@ +# Лаканічная кніга па TypeScript + +«Лаканічная кніга па TypeScript» прапануе поўны і змястоўны агляд магчымасцей TypeScript. У ёй прапануюцца зразумелыя тлумачэнні ўсіх аспектаў апошняй версіі мовы, ад магутнай сістэмы тыпаў да пашыраных функцый. Незалежна ад таго, пачатковец вы ці дасведчаны распрацоўшчык, гэтая кніга — неацэнны рэсурс для паляпшэння вашага разумення і валодання TypeScript. + +Гэтая кніга цалкам бясплатная і з адкрытым зыходным кодам. + +Я лічу, што якасная тэхнічная адукацыя павінна быць даступная ўсім, таму я раблю гэту кнігу бясплатнай і адкрытай. + +Калі кніга дапамагла вам разабрацца з памылкай, зразумець складаную канцэпцыю або прасунуцца па кар'ернай лесвіцы, калі ласка, падумайце аб падтрымцы маёй працы, заплаціўшы патрэбную суму (рэкамендаваная цана: 15 долараў ЗША) або спансуючы каву. Ваша падтрымка дапамагае мне абнаўляць змест і папаўняць яго новымі прыкладамі і больш глыбокімі тлумачэннямі. + +[![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) + +## Пераклады + +Гэтая кніга перакладзена на некалькі моў, у тым ліку: + +[Кітайская](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) + +[Італьянская](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) + +[Беларуская](https://github.com/gibbok/typescript-book/blob/main/README-be_BY.md) + +## Спампоўкі і сайт + +Вы таксама можаце спампаваць версію ў фармаце Epub: + +[https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) + +Інтэрнэт-версія даступная па адрасе: + +[https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) + +## Змест + + +- [Лаканічная кніга па TypeScript](#the-concise-typescript-book) + - [Пераклады](#translations) + - [Спампоўкі і сайт](#downloads-and-website) + - [Змест](#table-of-contents) + - [Уводзіны](#introduction) + - [Пра аўтара](#about-the-author) + - [Уводзіны ў TypeScript](#typescript-introduction) + - [Што такое TypeScript?](#what-is-typescript) + - [Чаму TypeScript?](#why-typescript) + - [TypeScript і JavaScript](#typescript-and-javascript) + - [Генерацыя кода TypeScript](#typescript-code-generation) + - [Сучасны JavaScript цяпер (Downleveling)](#modern-javascript-now-downleveling) + - [Пачатак працы з TypeScript](#getting-started-with-typescript) + - [Усталёўка](#installation) + - [Канфігурацыя](#configuration) + - [Файл канфігурацыі TypeScript](#typescript-configuration-file) + - [target](#target) + - [lib](#lib) + - [strict](#strict) + - [module](#module) + - [moduleResolution](#moduleresolution) + - [esModuleInterop](#esmoduleinterop) + - [jsx](#jsx) + - [skipLibCheck](#skiplibcheck) + - [files](#files) + - [include](#include) + - [exclude](#exclude) + - [importHelpers](#importhelpers) + - [Парады па міграцыі на TypeScript](#migration-to-typescript-advice) + - [Даследаванне сістэмы тыпаў](#exploring-the-type-system) + - [Моўны сэрвіс TypeScript](#the-typescript-language-service) + - [Структурная тыпізацыя](#structural-typing) + - [Фундаментальныя правілы параўнання TypeScript](#typescript-fundamental-comparison-rules) + - [Тыпы як мноствы](#types-as-sets) + - [Прысваенне тыпу: Дэкларацыі тыпаў і Сцвярджэнні тыпаў](#assign-a-type-type-declarations-and-type-assertions) + - [Дэкларацыя тыпу](#type-declaration) + - [Сцвярджэнне тыпу](#type-assertion) + - [Дэкларацыі асяроддзя (Ambient Declarations)](#ambient-declarations) + - [Праверка ўласцівасцей і Праверка лішніх уласцівасцей](#property-checking-and-excess-property-checking) + - [Слабыя тыпы](#weak-types) + - [Строгая праверка літэралаў аб'екта (Свежасць/Freshness)](#strict-object-literal-checking-freshness) + - [Вывад тыпаў](#type-inference) + - [Больш прасунуты вывад тыпаў](#more-advanced-inferences) + - [Пашырэнне тыпаў (Type Widening)](#type-widening) + - [Const](#const) + - [Мадыфікатар Const для параметраў тыпу](#const-modifier-on-type-parameters) + - [Сцвярджэнне Const (Const assertion)](#const-assertion) + - [Яўная анатацыя тыпу](#explicit-type-annotation) + - [Звужэнне тыпаў (Type Narrowing)](#type-narrowing) + - [Умовы](#conditions) + - [Выкід (Throwing) або вяртанне (returning)](#throwing-or-returning) + - [Дыскрымінаванае аб'яднанне (Discriminated Union)](#discriminated-union) + - [Карыстальніцкія вартаўнікі тыпу (User-Defined Type Guards)](#user-defined-type-guards) + - [Прымітыўныя тыпы](#primitive-types) + - [string](#string) + - [boolean](#boolean) + - [number](#number) + - [bigInt](#bigint) + - [Symbol](#symbol) + - [null і undefined](#null-and-undefined) + - [Array](#array) + - [any](#any) + - [Анатацыі тыпаў](#type-annotations) + - [Неабавязковыя ўласцівасці](#optional-properties) + - [Уласцівасці толькі для чытання](#readonly-properties) + - [Індэксныя сігнатуры](#index-signatures) + - [Пашырэнне тыпаў](#extending-types) + - [Літэральныя тыпы](#literal-types) + - [Літэральны вывад](#literal-inference) + - [strictNullChecks](#strictnullchecks) + - [Пералічэнні (Enums)](#enums) + - [Лікавыя пералічэнні](#numeric-enums) + - [Радковыя пералічэнні](#string-enums) + - [Канстантныя пералічэнні](#constant-enums) + - [Адваротнае адлюстраванне](#reverse-mapping) + - [Пералічэнні асяроддзя](#ambient-enums) + - [Вылічаныя і канстантныя члены](#computed-and-constant-members) + - [Звужэнне (Narrowing)](#narrowing) + - [Вартаўнікі тыпу typeof](#typeof-type-guards) + - [Звужэнне па праўдзівасці (Truthiness narrowing)](#truthiness-narrowing) + - [Звужэнне па роўнасці (Equality narrowing)](#equality-narrowing) + - [Звужэнне аператарам In](#in-operator-narrowing) + - [Звужэнне аператарам instanceof](#instanceof-narrowing) + - [Прысваенні](#assignments) + - [Аналіз патоку кіравання](#control-flow-analysis) + - [Прэдыкаты тыпаў](#type-predicates) + - [Дыскрымінаваныя аб'яднанні](#discriminated-unions) + - [Тып never](#the-never-type) + - [Праверка на вычарпальнасць](#exhaustiveness-checking) + - [Тыпы аб'ектаў](#object-types) + - [Тып картэжа (Ананімны)](#tuple-type-anonymous) + - [Іменаваны тып картэжа (Пазначаны)](#named-tuple-type-labeled) + - [Картэж фіксаванай даўжыні](#fixed-length-tuple) + - [Тып аб'яднання (Union Type)](#union-type) + - [Тыпы перасячэння (Intersection Types)](#intersection-types) + - [Індэксацыя тыпаў](#type-indexing) + - [Тып са значэння](#type-from-value) + - [Тып з вяртання функцыі](#type-from-func-return) + - [Тып з модуля](#type-from-module) + - [Супастаўленыя тыпы (Mapped Types)](#mapped-types) + - [Мадыфікатары супастаўленых тыпаў](#mapped-type-modifiers) + - [Умоўныя тыпы](#conditional-types) + - [Размеркавальныя ўмоўныя тыпы](#distributive-conditional-types) + - [Вывад тыпу infer ва ўмоўных тыпах](#infer-type-inference-in-conditional-types) + - [Наканаваныя ўмоўныя тыпы](#predefined-conditional-types) + - [Тыпы шаблонных аб'яднанняў](#template-union-types) + - [Тып Any](#any-type) + - [Тып Unknown](#unknown-type) + - [Тып Void](#void-type) + - [Тып Never](#never-type) + - [Інтэрфейс і тып](#interface-and-type) + - [Агульны сінтаксіс](#common-syntax) + - [Базавыя тыпы](#basic-types) + - [Аб'екты і інтэрфейсы](#objects-and-interfaces) + - [Тыпы аб'яднання і перасячэння](#union-and-intersection-types) + - [Убудаваныя прымітывы тыпаў](#built-in-type-primitives) + - [Агульныя ўбудаваныя аб'екты JS](#common-built-in-js-objects) + - [Перагрузкі](#overloads) + - [Аб'яднанне і пашырэнне](#merging-and-extension) + - [Адрозненні паміж тыпам і інтэрфейсам](#differences-between-type-and-interface) + - [Клас (Class)](#class) + - [Агульны сінтаксіс класа](#class-common-syntax) + - [Канструктар (Constructor)](#constructor) + - [Прыватныя і абароненыя канструктары](#private-and-protected-constructors) + - [Мадыфікатары доступу](#access-modifiers) + - [Get і Set (Гетэры і Сэтэры)](#get-and-set) + - [Аўтаматычныя аксэсары ў класах](#auto-accessors-in-classes) + - [this](#this) + - [Уласцівасці параметраў](#parameter-properties) + - [Абстрактныя класы](#abstract-classes) + - [З абагульненымі тыпамі (Generics)](#with-generics) + - [Дэкаратары](#decorators) + - [Дэкаратары класа](#class-decorators) + - [Дэкаратар уласцівасці](#property-decorator) + - [Дэкаратар метаду](#method-decorator) + - [Дэкаратары гетэра і сэтэра](#getter-and-setter-decorators) + - [Метаданыя дэкаратара](#decorator-metadata) + - [Успадкоўванне (Inheritance)](#inheritance) + - [Статычныя члены (Statics)](#statics) + - [Ініцыялізацыя ўласцівасцей](#property-initialization) + - [Перагрузка метадаў](#method-overloading) + - [Абагульненыя тыпы (Generics)](#generics) + - [Абагульнены тып](#generic-type) + - [Абагульненыя класы](#generic-classes) + - [Абмежаванні абагульненых тыпаў (Generic Constraints)](#generic-constraints) + - [Абагульненае кантэкстнае звужэнне](#generic-contextual-narrowing) + - [Сцёртыя структурныя тыпы](#erased-structural-types) + - [Прасторы імёнаў (Namespacing)](#namespacing) + - [Сімвалы (Symbols)](#symbols) + - [Дырэктывы Triple-Slash (Triple-Slash Directives)](#triple-slash-directives) + - [Маніпуляцыя тыпамі](#type-manipulation) + - [Стварэнне тыпаў з тыпаў](#creating-types-from-types) + - [Тыпы індэксаванага доступу](#indexed-access-types) + - [Утылітныя тыпы (Utility Types)](#utility-types) + - [Awaited\](#awaitedt) + - [Partial\](#partialt) + - [Required\](#requiredt) + - [Readonly\](#readonlyt) + - [Record\](#recordk-t) + - [Pick\](#pickt-k) + - [Omit\](#omitt-k) + - [Exclude\](#excludet-u) + - [Extract\](#extractt-u) + - [NonNullable\](#nonnullablet) + - [Parameters\](#parameterst) + - [ConstructorParameters\](#constructorparameterst) + - [ReturnType\](#returntypet) + - [InstanceType\](#instancetypet) + - [ThisParameterType\](#thisparametertypet) + - [OmitThisParameter\](#omitthisparametert) + - [ThisType\](#thistypet) + - [Uppercase\](#uppercaset) + - [Lowercase\](#lowercaset) + - [Capitalize\](#capitalizet) + - [Uncapitalize\](#uncapitalizet) + - [NoInfer\](#noinfert) + - [Іншае](#others) + - [Памылкі і апрацоўка выключэнняў](#errors-and-exception-handling) + - [Класы Mixin](#mixin-classes) + - [Асінхронныя магчымасці мовы](#asynchronous-language-features) + - [Ітэратары і генератары](#iterators-and-generators) + - [Даведнік па TsDocs JSDoc](#tsdocs-jsdoc-reference) + - [@types](#types) + - [JSX](#jsx-1) + - [Модулі ES6](#es6-modules) + - [Аператар узвядзення ў ступень ES7](#es7-exponentiation-operator) + - [Аператар for-await-of](#the-for-await-of-statement) + - [Метаўласцівасць new.target](#new-target-meta-property) + - [Выразы дынамічнага імпарту](#dynamic-import-expressions) + - ["tsc –watch"](#tsc-watch) + - [Аператар сцвярджэння non-null](#non-null-assertion-operator) + - [Дэкларацыі па змаўчанні](#defaulted-declarations) + - [Апцыянальны ланцужок (Optional Chaining)](#optional-chaining) + - [Аператар нулявога аб'яднання (Nullish coalescing)](#nullish-coalescing-operator) + - [Тыпы шаблонных літэралаў](#template-literal-types) + - [Перагрузка функцый](#function-overloading) + - [Рэкурсіўныя тыпы](#recursive-types) + - [Рэкурсіўныя ўмоўныя тыпы](#recursive-conditional-types) + - [Падтрымка модуляў ECMAScript у Node](#ecmascript-module-support-in-node) + - [Функцыі сцвярджэння (Assertion Functions)](#assertion-functions) + - [Варыятыўныя тыпы картэжаў (Variadic Tuple Types)](#variadic-tuple-types) + - [Упакаваныя тыпы (Boxed types)](#boxed-types) + - [Каварыянтнасць і контраварыянтнасць у TypeScript](#covariance-and-contravariance-in-typescript) + - [Неабавязковыя анатацыі варыянтнасці для параметраў тыпу](#optional-variance-annotations-for-type-parameters) + - [Шаблонныя радковыя індэксныя сігнатуры](#template-string-pattern-index-signatures) + - [Аператар satisfies](#the-satisfies-operator) + - [Імпарт і экспарт толькі тыпаў](#type-only-imports-and-export) + - [Дэкларацыя using і відавочнае кіраванне рэсурсамі](#using-declaration-and-explicit-resource-management) + - [Дэкларацыя await using](#await-using-declaration) + - [Атрыбуты імпарту (Import Attributes)](#import-attributes) + + +## Уводзіны + +Welcome to The Concise TypeScript Book! This guide equips you with essential knowledge and practical skills for effective TypeScript development. Discover key concepts and techniques to write clean, robust code. Whether you're a beginner or an experienced developer, this book serves as both a comprehensive guide and a handy reference for leveraging TypeScript's power in your projects. + +This book covers TypeScript 5.2. + +## Пра аўтара + +Сімонэ Паджалі — дасведчаны Staff Engineer, які захапляецца напісаннем прафесійнага кода з 90-х гадоў. На працягу сваёй міжнароднай кар'еры ён зрабіў унёсак у шматлікія праекты для шырокага кола кліентаў, ад стартапаў да буйных арганізацый. Такія вядомыя кампаніі, як HelloFresh, Siemens, O2, Leroy Merlin і Snowplow, скарысталіся яго вопытам і адданасцю справе. + +Звязацца з Сімонэ Паджалі можна на наступных платформах: + +* LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) +* GitHub: [https://github.com/gibbok](https://github.com/gibbok) +* X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) +* Email: gibbok.coding📧gmail.com + +## Уводзіны ў TypeScript + +### Што такое TypeScript? + +TypeScript — гэта строга тыпізаваная мова праграмавання, заснаваная на JavaScript. Яна была першапачаткова распрацавана Андэрсам Хейлсбергам у 2012 годзе і ў цяперашні час распрацоўваецца і падтрымліваецца Microsoft як праект з адкрытым зыходным кодам. + +TypeScript кампілюецца ў JavaScript і можа выконвацца ў любым асяроддзі выканання JavaScript (напрыклад, у браўзеры або на серверы Node.js). + +TypeScript падтрымлівае некалькі парадыгмаў праграмавання, такія як функцыянальнае, абагульненае (generic), імператыўнае і аб'ектна-арыентаванае. TypeScript не з'яўляецца ні інтэрпрэтаванай, ні кампіляванай (у традыцыйным сэнсе) мовай. + +### Чаму TypeScript? + +TypeScript — гэта строга тыпізаваная мова, якая дапамагае прадухіліць распаўсюджаныя памылкі праграмавання і пазбегнуць пэўных відаў памылак часу выканання да запуску праграмы. + +Строга тыпізаваная мова дазваляе распрацоўшчыку вызначаць розныя абмежаванні і паводзіны праграмы ў вызначэннях тыпаў даных, што палягчае праверку карэктнасці праграмнага забеспячэння і прадухіленне дэфектаў. Гэта асабліва каштоўна ў маштабных праграмах. + +Некаторыя перавагі TypeScript: + +* Статычная тыпізацыя, апцыянальна строгая +* Вывад тыпаў +* Доступ да функцый ES6 і ES7 +* Кросплатформенная і кросбраўзерная сумяшчальнасць +* Падтрымка інструментаў з дапамогай IntelliSense + +### TypeScript і JavaScript + +TypeScript пішацца ў файлах `.ts` ці `.tsx`, у той час як файлы JavaScript пішуцца ў `.js` ці `.jsx`. + +Файлы з пашырэннем `.tsx` ці `.jsx` могуць утрымліваць сінтаксічнае пашырэнне JavaScript JSX, якое выкарыстоўваецца ў React для распрацоўкі карыстальніцкага інтэрфейсу. + +TypeScript з'яўляецца тыпізаваным надмноствам JavaScript (ECMAScript 2015) з пункту гледжання сінтаксісу. Увесь код JavaScript з'яўляецца сапраўдным кодам TypeScript, але адваротнае не заўсёды верна. + +Напрыклад, разгледзім функцыю ў файле JavaScript з пашырэннем `.js`, такую як наступная: + + +```typescript +const sum = (a, b) => a + b; +``` + +Функцыю можна пераўтварыць і выкарыстоўваць у TypeScript, змяніўшы пашырэнне файла на `.ts`. Аднак, калі тая ж функцыя анатавана тыпамі TypeScript, яна не можа быць выканана ў любым асяроддзі выканання JavaScript без кампіляцыі. Наступны код TypeScript выкліча сінтаксічную памылку, калі ён не будзе скампіляваны: + + +```typescript +const sum = (a: number, b: number): number => a + b; +``` + +TypeScript быў распрацаваны для выяўлення магчымых выключэнняў, якія могуць узнікнуць падчас выканання, на этапе кампіляцыі, дазваляючы распрацоўшчыку вызначыць намер з дапамогай анатацый тыпаў. Акрамя таго, TypeScript таксама можа выяўляць праблемы, калі анатацыя тыпу не прадастаўлена. Напрыклад, наступны фрагмент кода не вызначае ніякіх тыпаў TypeScript: + + +```typescript +const items = [{ x: 1 }, { x: 2 }]; +const result = items.filter(item => item.y); +``` + +У гэтым выпадку TypeScript выяўляе памылку і паведамляе: + +```text +Property 'y' does not exist on type '{ x: number; }'. +``` + +Сістэма тыпаў TypeScript у значнай ступені залежыць ад паводзін JavaScript падчас выканання. Напрыклад, аператар складання (+), які ў JavaScript можа выконваць альбо канкатэнацыю радкоў, альбо складанне лікаў, мадэлюецца ў TypeScript такім жа чынам: + +```typescript +const result = '1' + 1; // Result is of type string +``` + +Каманда TypeScript прыняла свядомае рашэнне пазначаць незвычайнае выкарыстанне JavaScript як памылкі. Напрыклад, разгледзім наступны сапраўдны код JavaScript: + + +```typescript +const result = 1 + true; // In JavaScript, the result is equal 2 +``` + +Аднак TypeScript выдае памылку: + +```text +Operator '+' cannot be applied to types 'number' and 'boolean'. +``` + +Гэтая памылка ўзнікае, таму што TypeScript строга забяспечвае сумяшчальнасць тыпаў, і ў гэтым выпадку ён вызначае недапушчальную аперацыю паміж лікам і лагічным значэннем. + +### Генерацыя кода TypeScript + +Кампілятар TypeScript мае два асноўныя абавязкі: праверка на памылкі тыпаў і кампіляцыя ў JavaScript. Гэтыя два працэсы незалежныя адзін ад аднаго. Тыпы не ўплываюць на выкананне кода ў асяроддзі выканання JavaScript, бо яны цалкам сціраюцца падчас кампіляцыі. TypeScript усё яшчэ можа выводзіць JavaScript нават пры наяўнасці памылак тыпаў. +Вось прыклад кода TypeScript з памылкай тыпу: + + +```typescript +const add = (a: number, b: number): number => a + b; +const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. +``` + +Аднак ён усё яшчэ можа ствараць выканальны вывад JavaScript: + + +```typescript +'use strict'; +const add = (a, b) => a + b; +const result = add('x', 'y'); // xy +``` + +Немагчыма праверыць тыпы TypeScript падчас выканання. Напрыклад: + + +```typescript +interface Animal { + name: string; +} +interface Dog extends Animal { + bark: () => void; +} +interface Cat extends Animal { + meow: () => void; +} +const makeNoise = (animal: Animal) => { + if (animal instanceof Dog) { + // 'Dog' only refers to a type, but is being used as a value here. + // ... + } +}; +``` + +Паколькі тыпы сціраюцца пасля кампіляцыі, няма магчымасці запусціць гэты код у JavaScript. Каб распазнаць тыпы падчас выканання, нам трэба выкарыстаць іншы механізм. TypeScript прадастаўляе некалькі варыянтаў, адным з распаўсюджаных з'яўляецца «пазначанае аб'яднанне» (tagged union). Напрыклад: + +```typescript +interface Dog { + kind: 'dog'; // Tagged union + bark: () => void; +} +interface Cat { + kind: 'cat'; // Tagged union + meow: () => void; +} +type Animal = Dog | Cat; + +const makeNoise = (animal: Animal) => { + if (animal.kind === 'dog') { + animal.bark(); + } else { + animal.meow(); + } +}; + +const dog: Dog = { + kind: 'dog', + bark: () => console.log('bark'), +}; +makeNoise(dog); +``` + +Уласцівасць «kind» — гэта значэнне, якое можна выкарыстоўваць падчас выканання для адрознення аб'ектаў у JavaScript. + +Таксама магчыма, што значэнне падчас выканання будзе мець тып, адрозны ад заяўленага ў дэкларацыі тыпу. Напрыклад, калі распрацоўшчык няправільна інтэрпрэтаваў тып API і анатаваў яго няправільна. + +TypeScript з'яўляецца надмноствам JavaScript, таму ключавое слова `class` можа выкарыстоўвацца як тып і як значэнне падчас выканання. + +```typescript +class Animal { + constructor(public name: string) {} +} +class Dog extends Animal { + constructor( + public name: string, + public bark: () => void + ) { + super(name); + } +} +class Cat extends Animal { + constructor( + public name: string, + public meow: () => void + ) { + super(name); + } +} +type Mammal = Dog | Cat; + +const makeNoise = (mammal: Mammal) => { + if (mammal instanceof Dog) { + mammal.bark(); + } else { + mammal.meow(); + } +}; + +const dog = new Dog('Fido', () => console.log('bark')); +makeNoise(dog); +``` + +У JavaScript `class` мае ўласцівасць `prototype`, і аператар `instanceof` можа быць выкарыстаны для праверкі таго, ці з'яўляецца ўласцівасць прататыпа канструктара дзе-небудзь у ланцужку прататыпаў аб'екта. + +TypeScript не ўплывае на прадукцыйнасць выканання, паколькі ўсе тыпы будуць сцёртыя. Аднак TypeScript стварае некаторыя накладныя выдаткі падчас зборкі. + +### Сучасны JavaScript цяпер (Downleveling) + +TypeScript можа кампіляваць код у любую выпушчаную версію JavaScript, пачынаючы з ECMAScript 3 (1999). Гэта азначае, што TypeScript можа транспаляваць код з найноўшымі функцыямі JavaScript у больш старыя версіі, працэс, вядомы як Downleveling (паніжэнне ўзроўню). Гэта дазваляе выкарыстоўваць сучасны JavaScript, захоўваючы максімальную сумяшчальнасць са старымі асяроддзямі выканання. + +Важна адзначыць, што падчас транспаляцыі ў больш старую версію JavaScript, TypeScript можа генераваць код, які можа мець накладныя выдаткі на прадукцыйнасць у параўнанні з натыўнымі рэалізацыямі. + +Вось некаторыя з сучасных функцый JavaScript, якія можна выкарыстоўваць у TypeScript: + +* Модулі ECMAScript замест зваротных выклікаў у стылі AMD "define" або аператараў CommonJS "require". +* Класы замест прататыпаў. +* Аб'яўленне зменных з дапамогай "let" або "const" замест "var". +* Цыкл "for-of" або ".forEach" замест традыцыйнага цыкла "for". +* Стрэлачныя функцыі замест функцыянальных выразаў. +* Дэструктурызуючае прысвойванне. +* Скарочаныя назвы ўласцівасцей/метадаў і вылічаныя назвы ўласцівасцей. +* Параметры функцыі па змаўчанні. + +Выкарыстоўваючы гэтыя сучасныя функцыі JavaScript, распрацоўшчыкі могуць пісаць больш выразны і лаканічны код на TypeScript. + +## Пачатак працы з TypeScript + +### Усталёўка + +Visual Studio Code забяспечвае выдатную падтрымку мовы TypeScript, але не ўключае кампілятар TypeScript. Каб усталяваць кампілятар TypeScript, вы можаце выкарыстаць менеджэр пакетаў, напрыклад npm або yarn: + +```shell +npm install typescript --save-dev +``` + +або + +```shell +yarn add typescript --dev +``` + +Не забудзьцеся зафіксаваць (commit) згенераваны lock-файл, каб пераканацца, што кожны член каманды выкарыстоўвае адну і тую ж версію TypeScript. + +Каб запусціць кампілятар TypeScript, вы можаце выкарыстаць наступныя каманды: + +```shell +npx tsc +``` + +або + +```shell +yarn tsc +``` + +Рэкамендуецца ўсталёўваць TypeScript лакальна ў праект, а не глабальна, паколькі гэта забяспечвае больш прадказальны працэс зборкі. Аднак для аднаразовых выпадкаў можна выкарыстаць наступную каманду: + +```shell +npx tsc +``` + +або ўсталяваць яго глабальна: + +```shell +npm install -g typescript +``` + +Калі вы выкарыстоўваеце Microsoft Visual Studio, вы можаце атрымаць TypeScript як пакет у NuGet для вашых праектаў MSBuild. У кансолі дыспетчара пакетаў NuGet выканайце наступную каманду: + +```shell +Install-Package Microsoft.TypeScript.MSBuild +``` + +Падчас усталёўкі TypeScript усталёўваюцца два выканальныя файлы: «tsc» як кампілятар TypeScript і «tsserver» як аўтаномны сервер TypeScript. Аўтаномны сервер змяшчае кампілятар і моўныя сэрвісы, якія могуць выкарыстоўвацца рэдактарамі і IDE для забеспячэння інтэлектуальнага аўтазапаўнення кода. + +Акрамя таго, даступна некалькі TypeScript-сумяшчальных транспайлераў, такіх як Babel (праз плагін) або swc. Гэтыя транспайлеры могуць выкарыстоўвацца для пераўтварэння кода TypeScript у іншыя мэтавыя мовы або версіі. + +### Канфігурацыя + +TypeScript можна наладзіць з дапамогай параметраў каманднага радка tsc (CLI) або з выкарыстаннем спецыяльнага файла канфігурацыі пад назвай tsconfig.json, размешчанага ў корані праекта. + +Каб згенераваць файл tsconfig.json, папярэдне запоўнены рэкамендаванымі наладамі, вы можаце выкарыстаць наступную каманду: + +```shell +tsc --init +``` + +Пры выкананні каманды `tsc` лакальна, TypeScript скампілюе код, выкарыстоўваючы канфігурацыю, названую ў бліжэйшым файле tsconfig.json. + +Вось некалькі прыкладаў каманд CLI, якія запускаюцца з наладамі па змаўчанні: + +```shell +tsc main.ts // Compile a specific file (main.ts) to JavaScript +tsc src/*.ts // Compile any .ts files under the 'src' folder to JavaScript +tsc app.ts util.ts --outfile index.js // Compile two TypeScript files (app.ts and util.ts) into a single JavaScript file (index.js) +``` + +### Файл канфігурацыі TypeScript + +Файл tsconfig.json выкарыстоўваецца для канфігурацыі кампілятара TypeScript (tsc). Звычайна ён дадаецца ў корань праекта разам з файлам `package.json`. + +Заўвагі: + +* tsconfig.json прымае каментарыі, нават калі ён у фармаце json. +* Пажадана выкарыстоўваць гэты файл канфігурацыі замест параметраў каманднага радка. + +Па наступнай спасылцы вы можаце знайсці поўную дакументацыю і схему: + +[https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) + +[https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) + +Ніжэй прыведзены спіс распаўсюджаных і карысных канфігурацый: + +#### target + +Уласцівасць «target» выкарыстоўваецца для ўказання версіі JavaScript (версіі ECMAScript), у якую ваш TypeScript павінен быць згенераваны/скампіляваны. Для сучасных браўзераў добрым варыянтам з'яўляецца ES6, для старых браўзераў рэкамендуецца ES5. + +#### lib + +Уласцівасць «lib» выкарыстоўваецца для ўказання таго, якія файлы бібліятэк ўключаць падчас кампіляцыі. TypeScript аўтаматычна ўключае API для функцый, указаных ва ўласцівасці «target», але пры неабходнасці можна прапусціць або выбраць пэўныя бібліятэкі. Напрыклад, калі вы працуеце над серверным праектам, вы можаце выключыць бібліятэку «DOM», якая карысная толькі ў асяроддзі браўзера. + +#### strict + +Уласцівасць «strict» забяспечвае больш моцныя гарантыі і паляпшае бяспеку тыпаў. Пажадана заўсёды ўключаць гэтую ўласцівасць у файл tsconfig.json вашага праекта. Уключэнне ўласцівасці «strict» дазваляе TypeScript: + +* Генерыраваць код з выкарыстаннем "use strict" для кожнага зыходнага файла. +* Улічваць "null" і "undefined" у працэсе праверкі тыпаў. +* Адключаць выкарыстанне тыпу "any", калі адсутнічаюць анатацыі тыпаў. +* Выклікаць памылку пры выкарыстанні выразу "this", які ў іншым выпадку меў бы на ўвазе тып "any". + +#### module + +Уласцівасць «module» задае сістэму модуляў, якая падтрымліваецца для скампіляванай праграмы. Падчас выканання загрузчык модуляў выкарыстоўваецца для пошуку і выканання залежнасцей на аснове вызначанай сістэмы модуляў. + +Найбольш распаўсюджанымі загрузчыкамі модуляў, якія выкарыстоўваюцца ў JavaScript, з'яўляюцца Node.js CommonJS для серверных праграм і RequireJS для модуляў AMD у вэб-праграмах на аснове браўзера. TypeScript можа генераваць код для розных сістэм модуляў, у тым ліку UMD, System, ESNext, ES2015/ES6 і ES2020. + +Заўвага: Сістэму модуляў варта выбіраць зыходзячы з мэтавага асяроддзя і механізму загрузкі модуляў, даступнага ў гэтым асяроддзі. + +#### moduleResolution + +Уласцівасць «moduleResolution» вызначае стратэгію дазволу (resolution) модуляў. Выкарыстоўвайце «node» для сучаснага кода TypeScript, стратэгія «classic» выкарыстоўваецца толькі для старых версій TypeScript (да 1.6). + +#### esModuleInterop + +Уласцівасць «esModuleInterop» дазваляе імпартаваць па змаўчанні (default import) з модуляў CommonJS, якія не экспартаваліся з выкарыстаннем уласцівасці «default»; гэтая ўласцівасць забяспечвае праслойку (shim) для забеспячэння сумяшчальнасці ў згенераваным JavaScript. Пасля ўключэння гэтай опцыі мы можам выкарыстоўваць `import MyLibrary from "my-library"` замест `import * as MyLibrary from "my-library"`. + +#### jsx + +Уласцівасць «jsx» прымяняецца толькі да файлаў .tsx, якія выкарыстоўваюцца ў ReactJS, і кантралюе, як канструкцыі JSX кампілююцца ў JavaScript. Распаўсюджаным варыянтам з'яўляецца «preserve», які скампілюе ў файл .jsx, пакідаючы JSX нязменным, каб яго можна было перадаць іншым інструментам, такім як Babel, для далейшых пераўтварэнняў. + +#### skipLibCheck + +Уласцівасць «skipLibCheck» прадухіліць поўную праверку тыпаў імпартаваных старонніх пакетаў у TypeScript. Гэтая ўласцівасць скароціць час кампіляцыі праекта. TypeScript па-ранейшаму будзе правяраць ваш код на адпаведнасць азначэнням тыпаў, прадастаўленым гэтымі пакетамі. + +#### files + +Уласцівасць «files» паказвае кампілятару спіс файлаў, якія заўсёды павінны быць уключаны ў праграму. + +#### include + + +Уласцівасць «include» паказвае кампілятару спіс файлаў, якія мы хацелі б уключыць. Гэтая ўласцівасць дазваляе выкарыстоўваць glob-шаблоны, такія як «\*_» для любога падкаталога, «_» для любога імя файла і «?» для неабавязковых сімвалаў. + + +#### exclude + +Уласцівасць «exclude» паказвае кампілятару спіс файлаў, якія не павінны быць уключаны ў кампіляцыю. Сюды могуць уваходзіць такія файлы, як «node_modules» або тэставыя файлы. +Заўвага: tsconfig.json дазваляе каментарыі. + +### importHelpers + +TypeScript выкарыстоўвае дапаможны код пры генерацыі кода для пэўных пашыраных функцый або функцый JavaScript з паніжаным узроўнем (down-leveled). Па змаўчанні гэтыя памочнікі дублююцца ў файлах, якія іх выкарыстоўваюць. Опцыя `importHelpers` замест гэтага імпартуе гэтыя памочнікі з модуля `tslib`, што робіць вывад JavaScript больш эфектыўным. + +### Парады па міграцыі на TypeScript + +Для вялікіх праектаў рэкамендуецца прыняць паступовы пераход, калі код TypeScript і JavaScript першапачаткова будуць суіснаваць. Толькі невялікія праекты могуць быць перанесены на TypeScript за адзін раз. + +Першым крокам гэтага пераходу з'яўляецца ўкараненне TypeScript у працэс зборкі. Гэта можна зрабіць з дапамогай опцыі кампілятара «allowJs», якая дазваляе файлам .ts і .tsx суіснаваць з існуючымі файламі JavaScript. Паколькі TypeScript будзе вяртацца да тыпу «any» для зменнай, калі ён не можа вывесці тып з файлаў JavaScript, рэкамендуецца адключыць «noImplicitAny» у наладах кампілятара ў пачатку міграцыі. + +Другі крок — пераканацца, што вашы тэсты JavaScript працуюць разам з файламі TypeScript, каб вы маглі запускаць тэсты па меры пераўтварэння кожнага модуля. Калі вы выкарыстоўваеце Jest, падумайце аб выкарыстанні `ts-jest`, які дазваляе тэставаць праекты TypeScript з дапамогай Jest. + +Трэці крок — уключыць дэкларацыі тыпаў для старонніх бібліятэк у ваш праект. Гэтыя дэкларацыі можна знайсці ў камплекце з бібліятэкай альбо на DefinitelyTyped. Вы можаце шукаць іх з дапамогай [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) і ўсталёўваць з дапамогай: + +```shell +npm install --save-dev @types/package-name +``` + +або + +```shell +yarn add --dev @types/package-name. +``` + +Чацвёрты крок — міграваць модуль за модулем з падыходам «знізу ўверх», прытрымліваючыся графа залежнасцей, пачынаючы з лістоў. Ідэя складаецца ў тым, каб пачаць пераўтварэнне модуляў, якія не залежаць ад іншых модуляў. Для візуалізацыі графаў залежнасцей вы можаце выкарыстоўваць інструмент «madge». + +Добрымі кандыдатамі для гэтых першапачатковых пераўтварэнняў з'яўляюцца дапаможныя функцыі і код, звязаны са знешнімі API або спецыфікацыямі. Можна аўтаматычна генераваць азначэнні тыпаў TypeScript з кантрактаў Swagger, GraphQL або схем JSON для ўключэння ў ваш праект. + +Калі спецыфікацыі або афіцыйныя схемы недаступныя, вы можаце генераваць тыпы з неапрацаваных даных, такіх як JSON, вернуты серверам. Аднак рэкамендуецца генераваць тыпы са спецыфікацый, а не з даных, каб пазбегнуць пропуску пагранічных выпадкаў. + +Падчас міграцыі ўстрымлівайцеся ад рэфактарынгу кода і засяродзьцеся толькі на даданні тыпаў у свае модулі. + +Пяты крок — уключыць «noImplicitAny», што прымусіць усе тыпы быць вядомымі і вызначанымі, забяспечваючы лепшы вопыт TypeScript для вашага праекта. + +Падчас міграцыі вы можаце выкарыстоўваць дырэктыву `@ts-check`, якая ўключае праверку тыпаў TypeScript у файле JavaScript. Гэтая дырэктыва забяспечвае менш строгую версію праверкі тыпаў і можа быць выкарыстана першапачаткова для выяўлення праблем у файлах JavaScript. Калі `@ts-check` уключана ў файл, TypeScript паспрабуе вывесці азначэнні, выкарыстоўваючы каментарыі ў стылі JSDoc. Аднак разглядайце выкарыстанне анатацый JSDoc толькі на самым раннім этапе міграцыі. + +Падумайце аб тым, каб пакінуць значэнне па змаўчанні `noEmitOnError` у вашым tsconfig.json як false. Гэта дазволіць вам выводзіць зыходны код JavaScript, нават калі паведамляецца пра памылкі. + +## Даследаванне сістэмы тыпаў + +### Моўны сэрвіс TypeScript + +Моўны сэрвіс TypeScript, таксама вядомы як tsserver, прапануе розныя функцыі, такія як справаздача аб памылках, дыягностыка, кампіляцыя пры захаванні (compile-on-save), перайменаванне, пераход да азначэння (go to definition), спісы аўтазапаўнення, даведка па сігнатуры і многае іншае. Ён у асноўным выкарыстоўваецца інтэграванымі асяроддзямі распрацоўкі (IDE) для забеспячэння падтрымкі IntelliSense. Ён лёгка інтэгруецца з Visual Studio Code і выкарыстоўваецца такімі інструментамі, як Conquer of Completion (Coc). + +Распрацоўшчыкі могуць выкарыстоўваць спецыяльны API і ствараць свае ўласныя ўбудовы моўнага сэрвісу для паляпшэння вопыту рэдагавання TypeScript. Гэта можа быць асабліва карысна для рэалізацыі спецыяльных функцый лінтынгу (linting) або ўключэння аўтазапаўнення для карыстальніцкай мовы шаблонаў. + + +Прыкладам рэальнага карыстальніцкага плагіна з'яўляецца "typescript-styled-plugin", які забяспечвае паведамленне пра сінтаксічныя памылкі і падтрымку IntelliSense для ўласцівасцей CSS у кампанентах стылю (styled components). + + +Для атрымання дадатковай інфармацыі і кіраўніцтва па хуткім старце вы можаце звярнуцца да афіцыйнай вікі TypeScript на GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) + +### Структурная тыпізацыя + +TypeScript заснаваны на структурнай сістэме тыпаў. Гэта азначае, што сумяшчальнасць і эквівалентнасць тыпаў вызначаюцца фактычнай структурай або азначэннем тыпу, а не яго назвай або месцам аб'яўлення, як у намінатыўных сістэмах тыпаў, такіх як C# або C. + +Структурная сістэма тыпаў TypeScript была распрацавана на аснове таго, як дынамічная сістэма качынай тыпізацыі (duck typing) JavaScript працуе падчас выканання. + +Наступны прыклад з'яўляецца сапраўдным кодам TypeScript. Як вы можаце заўважыць, «X» і «Y» маюць адзін і той жа член «a», хоць яны маюць розныя назвы дэкларацый. Тыпы вызначаюцца іх структурамі, і ў гэтым выпадку, паколькі структуры аднолькавыя, яны сумяшчальныя і сапраўдныя. + +```typescript +type X = { + a: string; +}; +type Y = { + a: string; +}; +const x: X = { a: 'a' }; +const y: Y = x; // Valid +``` + +### Фундаментальныя правілы параўнання TypeScript + +Працэс параўнання ў TypeScript з'яўляецца рэкурсіўным і выконваецца для тыпаў, укладзеных на любым узроўні. + +Тып «X» сумяшчальны з «Y», калі «Y» мае па меншай меры тыя ж члены, што і «X». + +```typescript +type X = { + a: string; +}; +const y = { a: 'A', b: 'B' }; // Valid, as it has at least the same members as X +const r: X = y; +``` + +Параметры функцыі параўноўваюцца па тыпах, а не па іх назвах: + +```typescript +type X = (a: number) => void; +type Y = (a: number) => void; +let x: X = (j: number) => undefined; +let y: Y = (k: number) => undefined; +y = x; // Valid +x = y; // Valid +``` + +Тыпы вяртання функцый павінны быць аднолькавымі: + + +```typescript +type X = (a: number) => undefined; +type Y = (a: number) => number; +let x: X = (a: number) => undefined; +let y: Y = (a: number) => 1; +y = x; // Invalid +x = y; // Invalid +``` + +Тып вяртання зыходнай функцыі павінен быць падтыпам тыпу вяртання мэтавай функцыі: + + +```typescript +let x = () => ({ a: 'A' }); +let y = () => ({ a: 'A', b: 'B' }); +x = y; // Valid +y = x; // Invalid member b is missing +``` + +Адкідванне параметраў функцыі дапускаецца, бо гэта распаўсюджаная практыка ў JavaScript, напрыклад, пры выкарыстанні "Array.prototype.map()": + +```typescript +[1, 2, 3].map((element, _index, _array) => element + 'x'); +``` + +Такім чынам, наступныя дэкларацыі тыпаў цалкам сапраўдныя: + +```typescript +type X = (a: number) => undefined; +type Y = (a: number, b: number) => undefined; +let x: X = (a: number) => undefined; +let y: Y = (a: number) => undefined; // Missing b parameter +y = x; // Valid +``` + +Любыя дадатковыя неабавязковыя параметры зыходнага тыпу з'яўляюцца дапушчальнымі: + +```typescript +type X = (a: number, b?: number, c?: number) => undefined; +type Y = (a: number) => undefined; +let x: X = a => undefined; +let y: Y = a => undefined; +y = x; // Valid +x = y; //Valid +``` + +Любыя неабавязковыя параметры мэтавага тыпу без адпаведных параметраў у зыходным тыпе з'яўляюцца дапушчальнымі і не лічацца памылкай: + +```typescript +type X = (a: number) => undefined; +type Y = (a: number, b?: number) => undefined; +let x: X = a => undefined; +let y: Y = a => undefined; +y = x; // Valid +x = y; // Valid +``` + +Параметр rest (астатнія параметры) разглядаецца як бясконцая серыя неабавязковых параметраў: + +```typescript +type X = (a: number, ...rest: number[]) => undefined; +let x: X = a => undefined; //valid +``` + +Функцыі з перагрузкамі (overloads) з'яўляюцца сапраўднымі, калі сігнатура перагрузкі сумяшчальная з сігнатурай яе рэалізацыі: + + +```typescript +function x(a: string): void; +function x(a: string, b: number): void; +function x(a: string, b?: number): void { + console.log(a, b); +} +x('a'); // Valid +x('a', 1); // Valid + +function y(a: string): void; // Invalid, not compatible with implementation signature +function y(a: string, b: number): void; +function y(a: string, b: number): void { + console.log(a, b); +} +y('a'); +y('a', 1); +``` + +Параўнанне параметраў функцыі праходзіць паспяхова, калі зыходныя і мэтавыя параметры могуць быць прысвоены супертыпам або падтыпам (біварыянтнасць). + +```typescript +// Supertype +class X { + a: string; + constructor(value: string) { + this.a = value; + } +} +// Subtype +class Y extends X {} +// Subtype +class Z extends X {} + +type GetA = (x: X) => string; +const getA: GetA = x => x.a; + +// Bivariance does accept supertypes +console.log(getA(new X('x'))); // Valid +console.log(getA(new Y('Y'))); // Valid +console.log(getA(new Z('z'))); // Valid +``` + +Пералічэнні (Enums) параўнальныя і дапушчальныя з лікамі і наадварот, але параўнанне значэнняў Enum з розных тыпаў Enum недапушчальнае. + + +```typescript +enum X { + A, + B, +} +enum Y { + A, + B, + C, +} +const xa: number = X.A; // Valid +const ya: Y = 0; // Valid +X.A === Y.A; // Invalid +``` + +Экзэмпляры класа падлягаюць праверцы на сумяшчальнасць іх прыватных (private) і абароненых (protected) членаў: + + +```typescript +class X { + public a: string; + constructor(value: string) { + this.a = value; + } +} + +class Y { + private a: string; + constructor(value: string) { + this.a = value; + } +} + +let x: X = new Y('y'); // Invalid +``` + +Праверка параўнання не ўлічвае розную іерархію атрымання ў спадчыну (inheritance hierarchy), напрыклад: + +```typescript +class X { + public a: string; + constructor(value: string) { + this.a = value; + } +} +class Y extends X { + public a: string; + constructor(value: string) { + super(value); + this.a = value; + } +} +class Z { + public a: string; + constructor(value: string) { + this.a = value; + } +} +let x: X = new X('x'); +let y: Y = new Y('y'); +let z: Z = new Z('z'); +x === y; // Valid +x === z; // Valid even if z is from a different inheritance hierarchy +``` + +Абагульненыя тыпы (Generics) параўноўваюцца з выкарыстаннем іх структур на аснове выніковага тыпу пасля прымянення абагульненага параметра; параўноўваецца толькі канчатковы вынік як неабагульнены тып. + + +```typescript +interface X { + a: T; +} +let x: X = { a: 1 }; +let y: X = { a: 'a' }; +x === y; // Invalid as the type argument is used in the final structure +``` + +```typescript +interface X {} +const x: X = 1; +const y: X = 'a'; +x === y; // Valid as the type argument is not used in the final structure +``` + +Калі абагульненыя тыпы не маюць указанага аргумента тыпу, усе нявызначаныя аргументы разглядаюцца як тыпы з "any": + +```typescript +type X = (x: T) => T; +type Y = (y: K) => K; +let x: X = x => x; +let y: Y = y => y; +x = y; // Valid +``` + +Памятайце: + + +```typescript +let a: number = 1; +let b: number = 2; +a = b; // Valid, everything is assignable to itself + +let c: any; +c = 1; // Valid, all types are assignable to any + +let d: unknown; +d = 1; // Valid, all types are assignable to unknown + +let e: unknown; +let e1: unknown = e; // Valid, unknown is only assignable to itself and any +let e2: any = e; // Valid +let e3: number = e; // Invalid + +let f: never; +f = 1; // Invalid, nothing is assignable to never + +let g: void; +let g1: any; +g = 1; // Invalid, void is not assignable to or from anything expect any +g = g1; // Valid +``` + +Звярніце ўвагу, што калі ўключана "strictNullChecks", "null" і "undefined" разглядаюцца падобна да "void"; у адваротным выпадку яны падобныя на "never". + +### Тыпы як мноствы + +У TypeScript тып - гэта мноства магчымых значэнняў. Гэта мноства таксама называюць даменам (domain) тыпу. Кожнае значэнне тыпу можна разглядаць як элемент мноства. Тып усталёўвае абмежаванні, якім павінен задавальняць кожны элемент мноства, каб лічыцца членам гэтага мноства. +Асноўная задача TypeScript — праверыць, ці з'яўляецца адно мноства падмноствам іншага. + +TypeScript падтрымлівае розныя тыпы мностваў: + +| Тэрмін мноства | TypeScript | Заўвагі | +| ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| Пустое мноства (Empty set) | never | "never" не змяшчае нічога, акрамя сябе | +| Аднаэлементнае мноства (Single element set) | undefined / null / literal type | | +| Канечнае мноства (Finite set) | boolean / union | | +| Бясконцае мноства (Infinite set) | string / number / object | | +| Універсальнае мноства (Universal set) | any / unknown | Кожны элемент з'яўляецца членам "any", і кожнае мноства з'яўляецца яго падмноствам / "unknown" з'яўляецца тыпабяспечным аналагам "any" | + +Вось некалькі прыкладаў: + +| TypeScript | Тэрмін мноства | Прыклад | +| --------------------- | ---------------------- | ------------------------------------------------------------------------------- | +| never | ∅ (пустое мноства) | const x: never = 'x'; // Error: Type 'string' is not assignable to type 'never' | +| | | +| Literal type | Аднаэлементнае мноства | type X = 'X'; | +| | | type Y = 7; | +| | | +| Value assignable to T | Value ∈ T (член мноства) | type XY = 'X' \| 'Y'; | +| | | const x: XY = 'X'; | +| | | +| T1 assignable to T2 | T1 ⊆ T2 (падмноства) | type XY = 'X' \| 'Y'; | +| | | const x: XY = 'X'; | +| | | const j: XY = 'J'; // Type '"J"' is not assignable to type 'XY'. | +| | | | +| T1 extends T2 | T1 ⊆ T2 (падмноства) | type X = 'X' extends string ? true : false; | +| | | +| T1 \| T2 | T1 ∪ T2 (аб'яднанне) | type XY = 'X' \| 'Y'; | +| | | type JK = 1 \| 2; | +| | | +| T1 & T2 | T1 ∩ T2 (перасячэнне) | type X = \{ a: string \} | +| | | type Y = \{ b: string \} | +| | | type XY = X & Y | +| | | const x: XY = \{ a: 'a', b: 'b' \} | +| | | +| unknown | Універсальнае мноства | const x: unknown = 1 | + +Аб'яднанне (union), (T1 | T2) стварае больш шырокае мноства (абодва): + +```typescript +type X = { + a: string; +}; +type Y = { + b: string; +}; +type XY = X | Y; +const r: XY = { a: 'a', b: 'x' }; // Valid +``` + +Перасячэнне (intersection), (T1 & T2) стварае больш вузкае мноства (толькі агульныя): + + +```typescript +type X = { + a: string; +}; +type Y = { + a: string; + b: string; +}; +type XY = X & Y; +const r: XY = { a: 'a' }; // Invalid +const j: XY = { a: 'a', b: 'b' }; // Valid +``` + +Ключавое слова `extends` у гэтым кантэксце можна разглядаць як «падмноства». Яно ўсталёўвае абмежаванне для тыпу. `extends`, які выкарыстоўваецца з абагульненым тыпам (generic), бярэ абагульнены тып як бясконцае мноства і абмяжоўвае яго больш канкрэтным тыпам. +Звярніце ўвагу, што `extends` не мае нічога агульнага з іерархіяй у сэнсе ААП (у TypeScript няма такой канцэпцыі). +TypeScript працуе з мноствамі і не мае строгай іерархіі, на самай справе, як у прыкладзе ніжэй, два тыпы могуць перакрывацца, не з'яўляючыся пры гэтым падтыпам іншага тыпу (TypeScript улічвае структуру, форму аб'ектаў). + +```typescript +interface X { + a: string; +} +interface Y extends X { + b: string; +} +interface Z extends Y { + c: string; +} +const z: Z = { a: 'a', b: 'b', c: 'c' }; +interface X1 { + a: string; +} +interface Y1 { + a: string; + b: string; +} +interface Z1 { + a: string; + b: string; + c: string; +} +const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; + +const r: Z1 = z; // Valid +``` + +### Прысваенне тыпу: Дэкларацыі тыпаў і Сцвярджэнні тыпаў + +Тып можна прысвоіць рознымі спосабамі ў TypeScript: + +#### Дэкларацыя тыпу + +У наступным прыкладзе мы выкарыстоўваем x: X (": Type"), каб аб'явіць тып для зменнай x. + +```typescript +type X = { + a: string; +}; + +// Type declaration +const x: X = { + a: 'a', +}; +``` + +Калі зменная не ў паказаным фармаце, TypeScript паведаміць пра памылку. Напрыклад: + + +```typescript +type X = { + a: string; +}; + +const x: X = { + a: 'a', + b: 'b', // Error: Object literal may only specify known properties +}; +``` + +#### Сцвярджэнне тыпу + +Можна дадаць сцвярджэнне (assertion), выкарыстоўваючы ключавое слова `as`. Гэта паведамляе кампілятару, што распрацоўшчык мае больш інфармацыі пра тып, і заглушае любыя памылкі, якія могуць узнікнуць. + +Напрыклад: + +```typescript +type X = { + a: string; +}; +const x = { + a: 'a', + b: 'b', +} as X; +``` + +У прыведзеным вышэй прыкладзе сцвярджаецца, што аб'ект x мае тып X з выкарыстаннем ключавога слова as. Гэта паведамляе кампілятару TypeScript, што аб'ект адпавядае вызначанаму тыпу, нават калі ён мае дадатковую ўласцівасць b, якая адсутнічае ў азначэнні тыпу. + +Сцвярджэнні тыпу карысныя ў сітуацыях, калі неабходна ўказаць больш канкрэтны тып, асабліва пры працы з DOM. Напрыклад: + +```typescript +const myInput = document.getElementById('my_input') as HTMLInputElement; +``` + +Тут сцвярджэнне тыпу як HTMLInputElement выкарыстоўваецца, каб паведаміць TypeScript, што вынік getElementById павінен разглядацца як HTMLInputElement. +Сцвярджэнні тыпу таксама можна выкарыстоўваць для пераназвання ключоў, як паказана ў прыкладзе ніжэй з шаблоннымі літэраламі: + +```typescript +type J = { + [Property in keyof Type as `prefix_${string & + Property}`]: () => Type[Property]; +}; +type X = { + a: string; + b: number; +}; +type Y = J; +``` + +У гэтым прыкладзе тып `J` выкарыстоўвае супастаўлены (mapped) тып з шаблонным літэралам для пераназвання ключоў Type. Ён стварае новыя ўласцівасці з прэфіксам "prefix_", дададзеным да кожнага ключа, і іх адпаведныя значэнні з'яўляюцца функцыямі, якія вяртаюць зыходныя значэнні ўласцівасцей. + +Варта адзначыць, што пры выкарыстанні сцвярджэння тыпу TypeScript не будзе выконваць праверку лішніх уласцівасцей (excess property checking). Таму, як правіла, пераважней выкарыстоўваць дэкларацыю тыпу, калі структура аб'екта вядома загадзя. + +#### Дэкларацыі асяроддзя (Ambient Declarations) + +Дэкларацыі асяроддзя — гэта файлы, якія апісваюць тыпы для кода JavaScript, яны маюць фармат імя файла як `.d.ts.`. Звычайна яны імпартуюцца і выкарыстоўваюцца для анатавання існуючых бібліятэк JavaScript або для дадання тыпаў у існуючыя файлы JS у вашым праекце. + +Многія распаўсюджаныя тыпы бібліятэк можна знайсці па адрасе: +[https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) + +і могуць быць усталяваны з дапамогай: + +```shell +npm install --save-dev @types/library-name +``` + +Для вашых уласных дэкларацый асяроддзя вы можаце выкарыстоўваць імпарт з дапамогай спасылкі "triple-slash" (патройная касая рыса): + + +```typescript +/// +``` + +Вы можаце выкарыстоўваць дэкларацыі асяроддзя нават у файлах JavaScript, выкарыстоўваючы `// @ts-check`. + +Ключавое слова `declare` дазваляе вызначаць тыпы для існуючага кода JavaScript без яго імпарту, служачы запаўняльнікам для тыпаў з іншага файла або глабальна. + +### Праверка ўласцівасцей і Праверка лішніх уласцівасцей + +TypeScript заснаваны на структурнай сістэме тыпаў, але праверка лішніх уласцівасцей (Excess Property Checking) — гэта ўласцівасць TypeScript, якая дазваляе яму правяраць, ці мае аб'ект дакладныя ўласцівасці, указаныя ў тыпе. + +Праверка лішніх уласцівасцей выконваецца, напрыклад, пры прысваенні літэралаў аб'екта зменным або пры перадачы іх у якасці аргументаў функцыі. + + +```typescript +type X = { + a: string; +}; +const y = { a: 'a', b: 'b' }; +const x: X = y; // Valid because structural typing +const w: X = { a: 'a', b: 'b' }; // Invalid because excess property checking +``` + +### Слабыя тыпы + +Тып лічыцца слабым, калі ён утрымлівае толькі набор цалкам неабавязковых уласцівасцей: + +```typescript +type X = { + a?: string; + b?: string; +}; +``` + +TypeScript лічыць памылкай прысваенне чаго-небудзь слабаму тыпу, калі няма перакрыцця. Напрыклад, наступнае выклікае памылку: + + +```typescript +type Options = { + a?: string; + b?: string; +}; + +const fn = (options: Options) => undefined; + +fn({ c: 'c' }); // Invalid +``` + +Хоць і не рэкамендуецца, пры неабходнасці можна абыйсці гэтую праверку з дапамогай сцвярджэння тыпу: + +```typescript +type Options = { + a?: string; + b?: string; +}; +const fn = (options: Options) => undefined; +fn({ c: 'c' } as Options); // Valid +``` + +Або дадаўшы `unknown` да індэкснай сігнатуры ў слабым тыпе: + +```typescript +type Options = { + [prop: string]: unknown; + a?: string; + b?: string; +}; + +const fn = (options: Options) => undefined; +fn({ c: 'c' }); // Valid +``` + +### Строгая праверка літэралаў аб'екта (Свежасць/Freshness) + +Строгая праверка літэралаў аб'екта, якую часам называюць «свежасць» (freshness), — гэта функцыя ў TypeScript, якая дапамагае выявіць лішнія або напісаныя з памылкамі ўласцівасці, якія ў іншым выпадку засталіся б незаўважанымі пры звычайных структурных праверках тыпаў. + +Пры стварэнні літэрала аб'екта кампілятар TypeScript лічыць яго «свежым». Калі літэрал аб'екта прысвойваецца зменнай або перадаецца ў якасці параметра, TypeScript выдасць памылку, калі літэрал аб'екта вызначае ўласцівасці, якія не існуюць у мэтавым тыпе. + +Аднак «свежасць» знікае, калі літэрал аб'екта пашыраецца (widened) або выкарыстоўваецца сцвярджэнне тыпу. + +Вось некалькі прыкладаў для ілюстрацыі: + + +```typescript +type X = { a: string }; +type Y = { a: string; b: string }; + +let x: X; +x = { a: 'a', b: 'b' }; // Freshness check: Invalid assignment +var y: Y; +y = { a: 'a', bx: 'bx' }; // Freshness check: Invalid assignment + +const fn = (x: X) => console.log(x.a); + +fn(x); +fn(y); // Widening: No errors, structurally type compatible + +fn({ a: 'a', bx: 'b' }); // Freshness check: Invalid argument + +let c: X = { a: 'a' }; +let d: Y = { a: 'a', b: '' }; +c = d; // Widening: No Freshness check +``` + +### Вывад тыпаў + +TypeScript можа выводзіць тыпы, калі анатацыя не прадастаўлена, падчас: + +* Ініцыялізацыі зменнай. +* Ініцыялізацыі члена. +* Усталявання параметраў па змаўчанні. +* Вызначэння тыпу вяртання функцыі. + +Напрыклад: + +```typescript +let x = 'x'; // The type inferred is string +``` + +Кампілятар TypeScript аналізуе значэнне або выраз і вызначае яго тып на аснове даступнай інфармацыі. + +### Больш прасунуты вывад тыпаў + +Калі пры вывадзе тыпаў выкарыстоўваецца некалькі выразаў, TypeScript шукае «найлепшыя агульныя тыпы» (best common types). Напрыклад: + +```typescript +let x = [1, 'x', 1, null]; // The type inferred is: (string | number | null)[] +``` + +Калі кампілятар не можа знайсці найлепшыя агульныя тыпы, ён вяртае тып аб'яднання (union type). Напрыклад: + +```typescript +let x = [new RegExp('x'), new Date()]; // Type inferred is: (RegExp | Date)[] +``` + +TypeScript выкарыстоўвае «кантэкстную тыпізацыю» на аснове месцазнаходжання зменнай для вываду тыпаў. У наступным прыкладзе кампілятар ведае, што `e` мае тып `MouseEvent` з-за тыпу падзеі `click`, вызначанага ў файле lib.d.ts, які змяшчае дэкларацыі асяроддзя для розных распаўсюджаных канструкцый JavaScript і DOM: + +```typescript +window.addEventListener('click', function (e) {}); // The inferred type of e is MouseEvent +``` + +### Пашырэнне тыпаў (Type Widening) + +Пашырэнне тыпаў (Type widening) — гэта працэс, у якім TypeScript прысвойвае тып зменнай, якая ініцыялізуецца, калі анатацыя тыпу не была прадастаўлена. Ён дазваляе пераходзіць ад вузкіх тыпаў да больш шырокіх, але не наадварот. +У наступным прыкладзе: + + +```typescript +let x = 'x'; // TypeScript infers as string, a wide type +let y: 'y' | 'x' = 'y'; // y types is a union of literal types +y = x; // Invalid Type 'string' is not assignable to type '"x" | "y"'. +``` + +TypeScript прысвойвае `string` зменнай `x` на аснове адзіночнага значэння, прадстаўленага падчас ініцыялізацыі (`x`); гэта прыклад пашырэння. + +TypeScript дае магчымасць кантраляваць працэс пашырэння, напрыклад, з дапамогай "const". + +### Const + +Выкарыстанне ключавога слова `const` пры аб'яўленні зменнай прыводзіць да больш вузкага вываду тыпу ў TypeScript. + +Напрыклад: + +```typescript +const x = 'x'; // TypeScript infers the type of x as 'x', a narrower type +let y: 'y' | 'x' = 'y'; +y = x; // Valid: The type of x is inferred as 'x' +``` + +Дзякуючы выкарыстанню `const` для аб'яўлення зменнай x, яе тып звужаецца да канкрэтнага літэральнага значэння 'x'. Паколькі тып x звужаны, яго можна прысвоіць зменнай y без памылак. +Прычына, па якой тып можа быць выведзены, заключаецца ў тым, што зменныя `const` не могуць быць перапрысвоены, таму іх тып можа быць звужаны да пэўнага літэральнага тыпу, у дадзеным выпадку літэральнага тыпу 'x'. + +#### Мадыфікатар Const для параметраў тыпу + +Пачынаючы з версіі 5.0 TypeScript, можна ўказваць атрыбут `const` для абагульненага параметра тыпу. Гэта дазваляе вывесці найбольш дакладны магчымы тып. Давайце паглядзім на прыклад без выкарыстання `const`: + +```typescript +function identity(value: T) { + // No const here + return value; +} +const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: string; b: string; } +``` + +Як бачыце, уласцівасці `a` і `b` выводзяцца з тыпам `string`. + +Цяпер давайце паглядзім розніцу з версіяй `const`: + +```typescript +function identity(value: T) { + // Using const modifier on type parameters + return value; +} +const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: "a"; b: "b"; } +``` + +Цяпер мы бачым, што ўласцівасці `a` і `b` выводзяцца як `const`, таму `a` і `b` разглядаюцца як радковыя літэралы, а не проста як тыпы `string`. + +#### Сцвярджэнне Const (Const assertion) + +Гэтая функцыя дазваляе аб'яўляць зменную з больш дакладным літэральным тыпам на аснове яе значэння ініцыялізацыі, паказваючы кампілятару, што значэнне павінна разглядацца як нязменны літэрал. Вось некалькі прыкладаў: + +Для асобнай уласцівасці: + +```typescript +const v = { + x: 3 as const, +}; +v.x = 3; +``` + +Для цэлага аб'екта: + +```typescript +const v = { + x: 1, + y: 2, +} as const; +``` + +Гэта можа быць асабліва карысна пры вызначэнні тыпу для картэжа: + +```typescript +const x = [1, 2, 3]; // number[] +const y = [1, 2, 3] as const; // Tuple of readonly [1, 2, 3] +``` + +### Яўная анатацыя тыпу + +Мы можам быць канкрэтнымі і перадаць тып; у наступным прыкладзе ўласцівасць `x` мае тып `number`: + +```typescript +const v = { + x: 1, // Inferred type: number (widening) +}; +v.x = 3; // Valid +``` + +Мы можам зрабіць анатацыю тыпу больш канкрэтнай, выкарыстоўваючы аб'яднанне літэральных тыпаў: + + +```typescript +const v: { x: 1 | 2 | 3 } = { + x: 1, // x is now a union of literal types: 1 | 2 | 3 +}; +v.x = 3; // Valid +v.x = 100; // Invalid +``` + +### Звужэнне тыпаў (Type Narrowing) + +Звужэнне тыпаў (Type Narrowing) — гэта працэс у TypeScript, пры якім агульны тып звужаецца да больш канкрэтнага тыпу. Гэта адбываецца, калі TypeScript аналізуе код і вызначае, што пэўныя ўмовы або аперацыі могуць удакладніць інфармацыю пра тып. + +Звужэнне тыпаў можа адбывацца рознымі спосабамі, у тым ліку: + +#### Умовы + +Выкарыстоўваючы ўмоўныя аператары, такія як `if` або `switch`, TypeScript можа звузіць тып на аснове выніку ўмовы. Напрыклад: + +```typescript +let x: number | undefined = 10; + +if (x !== undefined) { + x += 100; // The type is number, which had been narrowed by the condition +} +``` + +#### Выкід (Throwing) або вяртанне (returning) + +Выкід памылкі або датэрміновае вяртанне з галіны могуць быць выкарыстаны, каб дапамагчы TypeScript звузіць тып. Напрыклад: + +```typescript +let x: number | undefined = 10; + +if (x === undefined) { + throw 'error'; +} +x += 100; +``` + +Іншыя спосабы звужэння тыпаў у TypeScript ўключаюць: + +* Аператар `instanceof`: Выкарыстоўваецца для праверкі таго, ці з'яўляецца аб'ект экзэмплярам пэўнага класа. +* Аператар `in`: Выкарыстоўваецца для праверкі таго, ці існуе ўласцівасць у аб'екце. +* Аператар `typeof`: Выкарыстоўваецца для праверкі тыпу значэння падчас выканання. +* Убудаваныя функцыі, такія як `Array.isArray()`: Выкарыстоўваюцца для праверкі таго, ці з'яўляецца значэнне масівам. + +#### Дыскрымінаванае аб'яднанне (Discriminated Union) + +Выкарыстанне «дыскрымінаванага аб'яднання» — гэта шаблон у TypeScript, дзе да аб'ектаў дадаецца відавочны «тэг» (tag), каб адрозніваць розныя тыпы ўнутры аб'яднання. Гэты шаблон таксама называюць «пазначанае аб'яднанне» (tagged union). У наступным прыкладзе «тэг» прадстаўлены ўласцівасцю «type»: + +```typescript +type A = { type: 'type_a'; value: number }; +type B = { type: 'type_b'; value: string }; + +const x = (input: A | B): string | number => { + switch (input.type) { + case 'type_a': + return input.value + 100; // type is A + case 'type_b': + return input.value + 'extra'; // type is B + } +}; +``` + +#### Карыстальніцкія вартаўнікі тыпу (User-Defined Type Guards) + +У выпадках, калі TypeScript не можа вызначыць тып, можна напісаць дапаможную функцыю, вядомую як «карыстальніцкі вартаўнік тыпу». У наступным прыкладзе мы будзем выкарыстоўваць прэдыкат тыпу (Type Predicate), каб звузіць тып пасля прымянення пэўнай фільтрацыі: + +```typescript +const data = ['a', null, 'c', 'd', null, 'f']; + +const r1 = data.filter(x => x != null); // The type is (string | null)[], TypeScript was not able to infer the type properly + +const isValid = (item: string | null): item is string => item !== null; // Custom type guard + +const r2 = data.filter(isValid); // The type is fine now string[], by using the predicate type guard we were able to narrow the type +``` + +## Прымітыўныя тыпы + +TypeScript падтрымлівае 7 прымітыўных тыпаў. Прымітыўны тып даных адносіцца да тыпу, які не з'яўляецца аб'ектам і не мае звязаных з ім метадаў. У TypeScript усе прымітыўныя тыпы з'яўляюцца нязменнымі (immutable), што азначае, што іх значэнні не могуць быць зменены пасля прысваення. + +### string + +Прымітыўны тып `string` захоўвае тэкставыя даныя, і значэнне заўсёды бярэцца ў двайныя або адзінарныя двукоссі. + +```typescript +const x: string = 'x'; +const y: string = 'y'; +``` + +Радкі могуць займаць некалькі радкоў, калі яны акружаны сімвалам зваротнага апострафа (`): + +```typescript +let sentence: string = `xxx, + yyy`; +``` + +### boolean + +Тып даных `boolean` у TypeScript захоўвае бінарнае значэнне, альбо `true`, альбо `false`. + +```typescript +const isReady: boolean = true; +``` + +### number + +Тып даных `number` у TypeScript прадстаўлены 64-бітным значэннем з плывучай коскай. Тып `number` можа прадстаўляць цэлыя лікі і дробы. +TypeScript таксама падтрымлівае шаснаццатковыя, двайковыя і васьмярковыя лікі, напрыклад: + +```typescript +const decimal: number = 10; +const hexadecimal: number = 0xa00d; // Hexadecimal starts with 0x +const binary: number = 0b1010; // Binary starts with 0b +const octal: number = 0o633; // Octal starts with 0o +``` + +### bigInt + +`bigInt` прадстаўляе лікавыя значэнні, якія з'яўляюцца вельмі вялікімі (2^53 – 1) і не могуць быць прадстаўлены з дапамогай `number`. + +`bigInt` можа быць створаны выклікам убудаванай функцыі `BigInt()` або даданнем `n` у канец любога цэлага лікавага літэрала: + +```typescript +const x: bigint = BigInt(9007199254740991); +const y: bigint = 9007199254740991n; +``` + +Заўвагі: + +* Значэнні `bigInt` нельга змешваць з `number` і выкарыстоўваць з убудаваным `Math`; яны павінны быць прыведзены да аднаго тыпу. +* Значэнні `bigInt` даступныя толькі калі мэтавая канфігурацыя — ES2020 або вышэй. + +### Symbol + +Сімвалы (Symbols) — гэта ўнікальныя ідэнтыфікатары, якія могуць выкарыстоўвацца ў якасці ключоў уласцівасцей у аб'ектах для прадухілення канфліктаў імёнаў. + +```typescript +type Obj = { + [sym: symbol]: number; +}; + +const a = Symbol('a'); +const b = Symbol('b'); +let obj: Obj = {}; +obj[a] = 123; +obj[b] = 456; + +console.log(obj[a]); // 123 +console.log(obj[b]); // 456 +``` + +### null and undefined + +Тыпы `null` і `undefined` абодва прадстаўляюць адсутнасць значэння. + +Тып `undefined` азначае, што значэнне не прысвоена ці не ініцыялізавана, альбо паказвае на ненаўмысную адсутнасць значэння. + +Тып `null` азначае, што мы ведаем, што поле не мае значэння, таму значэнне недаступна; гэта паказвае на наўмысную адсутнасць значэння. + +### Array + +`Array` (масіў) — гэта тып даных, які можа захоўваць некалькі значэнняў аднаго або розных тыпаў. Яго можна вызначыць з дапамогай наступнага сінтаксісу: + +```typescript +const x: string[] = ['a', 'b']; +const y: Array = ['a', 'b']; +const j: Array = ['a', 1, 'b', 2]; // Union +``` + +TypeScript падтрымлівае масівы толькі для чытання (readonly arrays), выкарыстоўваючы наступны сінтаксіс: + + +```typescript +const x: readonly string[] = ['a', 'b']; // Readonly modifier +const y: ReadonlyArray = ['a', 'b']; +const j: ReadonlyArray = ['a', 1, 'b', 2]; +j.push('x'); // Invalid +``` + +TypeScript падтрымлівае картэжы (tuple) і картэжы толькі для чытання: + +```typescript +const x: [string, number] = ['a', 1]; +const y: readonly [string, number] = ['a', 1]; +``` + +### any + +Тып даных `any` літаральна прадстаўляе «любое» значэнне; гэта значэнне па змаўчанні, калі TypeScript не можа вывесці тып або ён не паказаны. + +Пры выкарыстанні `any` кампілятар TypeScript прапускае праверку тыпаў, таму пры выкарыстанні `any` адсутнічае бяспека тыпаў. Як правіла, не выкарыстоўвайце `any`, каб заглушыць кампілятар пры ўзнікненні памылкі; замест гэтага засяродзьцеся на выпраўленні памылкі, паколькі пры выкарыстанні `any` можна парушыць кантракты, і мы губляем перавагі аўтазапаўнення TypeScript. + +Тып `any` можа быць карысны падчас паступовай міграцыі з JavaScript на TypeScript, бо ён можа заглушыць кампілятар. + +Для новых праектаў выкарыстоўвайце наладу TypeScript `noImplicitAny`, якая дазваляе TypeScript выдаваць памылкі там, дзе выкарыстоўваецца або выводзіцца `any`. + +Тып `any` звычайна з'яўляецца крыніцай памылак, якія могуць маскіраваць рэальныя праблемы з вашымі тыпамі. Пазбягайце яго выкарыстання, наколькі гэта магчыма. + +## Type Annotations + +On variables declared using `var`, `let` and `const`, it is possible to optionally add a type: + +```typescript +const x: number = 1; +``` + +TypeScript does a good job of inferring types, especially when simple one, so these declarations in most cases are not necessary. + +On functions is possible to add type annotations to parameters: + +```typescript +function sum(a: number, b: number) { + return a + b; +} +``` + +The following is an example using a anonymous functions (so called lambda function): + +```typescript +const sum = (a: number, b: number) => a + b; +``` + +These annotation can be avoided when a default value for a parameter is present: + +```typescript +const sum = (a = 10, b: number) => a + b; +``` + +Return type annotations can be added to functions: + +```typescript +const sum = (a = 10, b: number): number => a + b; +``` + +This is useful especially for more complex functions as writing expliciting the return type before an implementation can help better think about the function. + +Generally consider annotating type signatures but not the body local variables and add types always to object literals. + +## Optional Properties + +An object can specify Optional Properties by adding a question mark `?` to the end of the property name: + +```typescript +type X = { + a: number; + b?: number; // Optional +}; +``` + +It is possible to specify a default value when a property is optional" + +```typescript +type X = { + a: number; + b?: number; +}; +const x = ({ a, b = 100 }: X) => a + b; +``` + +## Readonly Properties + +Is it possible to prevent writing on a property by using the modifier `readonly`which makes sure that the property cannot be re-written but does not provide any guarantee of total immutability: + +```typescript +interface Y { + readonly a: number; +} + +type X = { + readonly a: number; +}; + +type J = Readonly<{ + a: number; +}>; + +type K = { + readonly [index: number]: string; +}; +``` + +## Index Signatures + +In TypeScript we can use as index signature `string`, `number`, and `symbol`: + +```typescript +type K = { + [name: string | number]: string; +}; +const k: K = { x: 'x', 1: 'b' }; +console.log(k['x']); +console.log(k[1]); +console.log(k['1']); // Same result as k[1] +``` + +Please note that JavaScript automatically converts an index with `number` to an index with `string` so `k[1]` or `k["1"]` return the same value. + +## Extending Types + +It is possible to extend an `interface` (copy members from another type): + +```typescript +interface X { + a: string; +} +interface Y extends X { + b: string; +} +``` + +It is also possible to extend from multiple types: + +```typescript +interface A { + a: string; +} +interface B { + b: string; +} +interface Y extends A, B { + y: string; +} +``` + +The `extends` keyword works only on interfaces and classes, for types use an intersection: + +```typescript +type A = { + a: number; +}; +type B = { + b: number; +}; +type C = A & B; +``` + +It is possible to extend a type using an inference but not vice versa: + +```typescript +type A = { + a: string; +}; +interface B extends A { + b: string; +} +``` + +## Literal Types + +A Literal Type is a single element set from a collective type, it defines a very exact value that is a JavaScript primitive. + +Literal Types in TypeScript are numbers, strings, and booleans. + +Example of literals: + +```typescript +const a = 'a'; // String literal type +const b = 1; // Numeric literal type +const c = true; // Boolean literal type +``` + +String, Numeric, and Boolean Literal Types are used in the union, type guard, and type aliases. +In the following example you can see a type alias union, `O` can be the only value specified and not any other string: + +```typescript +type O = 'a' | 'b' | 'c'; +``` + +## Literal Inference + +Literal Inference is a feature in TypeScript that allows the type of a variable or parameter to be inferred based on its value. + +In the following example we can see that TypeScript considers `x` a literal type as the value cannot be changed any time later, when instead `y` is inferred as string as it can be modified any time later. + +```typescript +const x = 'x'; // Literal type of 'x', because this value cannot be changed +let y = 'y'; // Type string, as we can change this value +``` + +In the following example we can see that `o.x` was inferred as a `string` (and not a literal of `a`) as TypeScript considers that the value can be changed any time later. + + +```typescript +type X = 'a' | 'b'; + +let o = { + x: 'a', // This is a wider string +}; + +const fn = (x: X) => `${x}-foo`; + +console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' +``` + +As you can see the code throws an error when passing `o.x` to `fn` as X is a narrower type. + +We can solve this issue by using type assertion using `const` or the `X` type: + + +```typescript +let o = { + x: 'a' as const, +}; +``` + +or: + + +```typescript +let o = { + x: 'a' as X, +}; +``` + +## strictNullChecks + +`strictNullChecks` is a TypeScript compiler option that enforces strict null checking. When this option is enabled, variables and parameters can only be assigned `null` or `undefined` if they have been explicitly declared to be of that type using the union type `null` | `undefined`. If a variable or parameter is not explicitly declared as nullable, TypeScript will generate an error to prevent potential runtime errors. + +## Enums + +In TypeScript, an `enum` is a set of named constant values. + +```typescript +enum Color { + Red = '#ff0000', + Green = '#00ff00', + Blue = '#0000ff', +} +``` + +Enums can be defined in different ways: + +### Numeric enums + +In TypeScript, a Numeric Enum is an Enum where each constant is assigned a numeric value, starting from 0 by default. + +```typescript +enum Size { + Small, // value starts from 0 + Medium, + Large, +} +``` + +It is possible to specify custom values by explicitly assigning them: + +```typescript +enum Size { + Small = 10, + Medium, + Large, +} +console.log(Size.Medium); // 11 +``` + +### String enums + +In TypeScript, a String enum is an Enum where each constant is assigned a string value. + +```typescript +enum Language { + English = 'EN', + Spanish = 'ES', +} +``` + +Note: TypeScript allows the usage of heterogeneous Enums where string and numeric members can coexist. + +### Constant enums + +A constant enum in TypeScript is a special type of Enum where all the values are known at compile time and are inlined wherever the enum is used, resulting in more efficient code. + +```typescript +const enum Language { + English = 'EN', + Spanish = 'ES', +} +console.log(Language.English); +``` + +Will be compiled into: + +```typescript +console.log('EN' /* Language.English */); +``` + +Notes: +Const Enums have hardcoded values, erasing the Enum, which can be more efficient in self-contained libraries but is generally not desirable. Also, Const enums cannot have computed members. + +### Reverse mapping + +In TypeScript, reverse mappings in Enums refer to the ability to retrieve the Enum member name from its value. By default, Enum members have forward mappings from name to value, but reverse mappings can be created by explicitly setting values for each member. Reverse mappings are useful when you need to look up an Enum member by its value, or when you need to iterate over all the Enum members. Note that only numeric enums members will generate reverse mappings, while String Enum members do not get a reverse mapping generated at all. + +The following enum: + +```typescript +enum Grade { + A = 90, + B = 80, + C = 70, + F = 'fail', +} +``` + +Compiles to: + + +```javascript +'use strict'; +var Grade; +(function (Grade) { + Grade[(Grade['A'] = 90)] = 'A'; + Grade[(Grade['B'] = 80)] = 'B'; + Grade[(Grade['C'] = 70)] = 'C'; + Grade['F'] = 'fail'; +})(Grade || (Grade = {})); +``` + +Therefore, mapping values to keys works for numeric enum members, but not for string enum members: + + +```typescript +enum Grade { + A = 90, + B = 80, + C = 70, + F = 'fail', +} +const myGrade = Grade.A; +console.log(Grade[myGrade]); // A +console.log(Grade[90]); // A + +const failGrade = Grade.F; +console.log(failGrade); // fail +console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. +``` + +### Ambient enums + +An ambient enum in TypeScript is a type of Enum that is defined in a declaration file (*.d.ts) without an associated implementation. It allows you to define a set of named constants that can be used in a type-safe way across different files without having to import the implementation details in each file. + +### Computed and constant members + +In TypeScript, a computed member is a member of an Enum that has a value calculated at runtime, while a constant member is a member whose value is set at compile-time and cannot be changed during runtime. Computed members are allowed in regular Enums, while constant members are allowed in both regular and const enums. + +```typescript +// Constant members +enum Color { + Red = 1, + Green = 5, + Blue = Red + Green, +} +console.log(Color.Blue); // 6 generation at compilation time +``` + +```typescript +// Computed members +enum Color { + Red = 1, + Green = Math.pow(2, 2), + Blue = Math.floor(Math.random() * 3) + 1, +} +console.log(Color.Blue); // random number generated at run time +``` + +Enums are denoted by unions comprising their member types. The values of each member can be determined through constant or non-constant expressions, with members possessing constant values being assigned literal types. To illustrate, consider the declaration of type E and its subtypes E.A, E.B, and E.C. In this case, E represents the union E.A | E.B | E.C. + +```typescript +const identity = (value: number) => value; + +enum E { + A = 2 * 5, // Numeric literal + B = 'bar', // String literal + C = identity(42), // Opaque computed +} + +console.log(E.C); //42 +``` + +## Narrowing + +TypeScript narrowing is the process of refining the type of a variable within a conditional block. This is useful when working with union types, where a variable can have more than one type. + +TypeScript recognizes several ways to narrow the type: + +### typeof type guards + +The typeof type guard is one specific type guard in TypeScript that checks the type of a variable based on its built-in JavaScript type. + +```typescript +const fn = (x: number | string) => { + if (typeof x === 'number') { + return x + 1; // x is number + } + return -1; +}; +``` + +### Truthiness narrowing + +Truthiness narrowing in TypeScript works by checking whether a variable is truthy or falsy to narrow its type accordingly. + +```typescript +const toUpperCase = (name: string | null) => { + if (name) { + return name.toUpperCase(); + } else { + return null; + } +}; +``` + +### Equality narrowing + +Equality narrowing in TypeScript works by checking whether a variable is equal to a specific value or not, to narrow its type accordingly. + +It is used in conjunction with `switch` statements and equality operators such as `===`, `!==`, `==`, and `!=` to narrow down types. + +```typescript +const checkStatus = (status: 'success' | 'error') => { + switch (status) { + case 'success': + return true; + case 'error': + return null; + } +}; +``` + +### In Operator narrowing + +The `in` Operator narrowing in TypeScript is a way to narrow the type of a variable based on whether a property exists within the variable's type. + +```typescript +type Dog = { + name: string; + breed: string; +}; + +type Cat = { + name: string; + likesCream: boolean; +}; + +const getAnimalType = (pet: Dog | Cat) => { + if ('breed' in pet) { + return 'dog'; + } else { + return 'cat'; + } +}; +``` + +### instanceof narrowing + +The `instanceof` operator narrowing in TypeScript is a way to narrow the type of a variable based on its constructor function, by checking if an object is an instance of a certain class or interface. + +```typescript +class Square { + constructor(public width: number) {} +} +class Rectangle { + constructor( + public width: number, + public height: number + ) {} +} +function area(shape: Square | Rectangle) { + if (shape instanceof Square) { + return shape.width * shape.width; + } else { + return shape.width * shape.height; + } +} +const square = new Square(5); +const rectangle = new Rectangle(5, 10); +console.log(area(square)); // 25 +console.log(area(rectangle)); // 50 +``` + +## Assignments + +TypeScript narrowing using assignments is a way to narrow the type of a variable based on the value assigned to it. When a variable is assigned a value, TypeScript infers its type based on the assigned value, and it narrows the type of the variable to match the inferred type. + +```typescript +let value: string | number; +value = 'hello'; +if (typeof value === 'string') { + console.log(value.toUpperCase()); +} +value = 42; +if (typeof value === 'number') { + console.log(value.toFixed(2)); +} +``` + +## Control Flow Analysis + +Control Flow Analysis in TypeScript is a way to statically analyze the code flow to infer the types of variables, allowing the compiler to narrow the types of those variables as needed, based on the results of the analysis. + +Prior to TypeScript 4.4, code flow analysis would only be applied to code within an if statement, but from TypeScript 4.4, it can also be applied to conditional expressions and discriminant property accesses indirectly referenced through const variables. + +For example: + +```typescript +const f1 = (x: unknown) => { + const isString = typeof x === 'string'; + if (isString) { + x.length; + } +}; + +const f2 = ( + obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } +) => { + const isFoo = obj.kind === 'foo'; + if (isFoo) { + obj.foo; + } else { + obj.bar; + } +}; +``` + +Some examples where narrowing does not occur: + + +```typescript +const f1 = (x: unknown) => { + let isString = typeof x === 'string'; + if (isString) { + x.length; // Error, no narrowing because isString it is not const + } +}; + +const f6 = ( + obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } +) => { + const isFoo = obj.kind === 'foo'; + obj = obj; + if (isFoo) { + obj.foo; // Error, no narrowing because obj is assigned in function body + } +}; +``` + +Notes: Up to five levels of indirection are analyzed in conditional expressions. + +## Type Predicates + +Type Predicates in TypeScript are functions that return a boolean value and are used to narrow the type of a variable to a more specific type. + +```typescript +const isString = (value: unknown): value is string => typeof value === 'string'; + +const foo = (bar: unknown) => { + if (isString(bar)) { + console.log(bar.toUpperCase()); + } else { + console.log('not a string'); + } +}; +``` + +## Discriminated Unions + +Discriminated Unions in TypeScript are a type of union type that uses a common property, known as the discriminant, to narrow down the set of possible types for the union. + +```typescript +type Square = { + kind: 'square'; // Discriminant + size: number; +}; + +type Circle = { + kind: 'circle'; // Discriminant + radius: number; +}; + +type Shape = Square | Circle; + +const area = (shape: Shape) => { + switch (shape.kind) { + case 'square': + return Math.pow(shape.size, 2); + case 'circle': + return Math.PI * Math.pow(shape.radius, 2); + } +}; + +const square: Square = { kind: 'square', size: 5 }; +const circle: Circle = { kind: 'circle', radius: 2 }; + +console.log(area(square)); // 25 +console.log(area(circle)); // 12.566370614359172 +``` + +## The never Type + +When a variable is narrowed to a type that cannot contain any values, the TypeScript compiler will infer that the variable must be of the `never` type. This is because The never Type represents a value that can never be produced. + +```typescript +const printValue = (val: string | number) => { + if (typeof val === 'string') { + console.log(val.toUpperCase()); + } else if (typeof val === 'number') { + console.log(val.toFixed(2)); + } else { + // val has type never here because it can never be anything other than a string or a number + const neverVal: never = val; + console.log(`Unexpected value: ${neverVal}`); + } +}; +``` + +## Exhaustiveness checking + +Exhaustiveness checking is a feature in TypeScript that ensures all possible cases of a discriminated union are handled in a `switch` statement or an `if` statement. + +```typescript +type Direction = 'up' | 'down'; + +const move = (direction: Direction) => { + switch (direction) { + case 'up': + console.log('Moving up'); + break; + case 'down': + console.log('Moving down'); + break; + default: + const exhaustiveCheck: never = direction; + console.log(exhaustiveCheck); // This line will never be executed + } +}; +``` + +The `never` type is used to ensure that the default case is exhaustive and that TypeScript will raise an error if a new value is added to the Direction type without being handled in the switch statement. + +## Object Types + +In TypeScript, object types describe the shape of an object. They specify the names and types of the object's properties, as well as whether those properties are required or optional. + +In TypeScript, you can define object types in two primary ways: + +Interface which defines the shape of an object by specifying the names, types, and optionality of its properties. + +```typescript +interface User { + name: string; + age: number; + email?: string; +} +``` + +Type alias, similar to an interface, defines the shape of an object. However, it can also create a new custom type that is based on an existing type or a combination of existing types. This includes defining union types, intersection types, and other complex types. + +```typescript +type Point = { + x: number; + y: number; +}; +``` + +It also possible to define a type anonymously: + +```typescript +const sum = (x: { a: number; b: number }) => x.a + x.b; +console.log(sum({ a: 5, b: 1 })); +``` + +## Tuple Type (Anonymous) + +A Tuple Type is a type that represents an array with a fixed number of elements and their corresponding types. A tuple type enforces a specific number of elements and their respective types in a fixed order. Tuple types are useful when you want to represent a collection of values with specific types, where the position of each element in the array has a specific meaning. + +```typescript +type Point = [number, number]; +``` + +## Named Tuple Type (Labeled) + +Tuple types can include optional labels or names for each element. These labels are for readability and tooling assistance, and do not affect the operations you can perform with them. + +```typescript +type T = string; +type Tuple1 = [T, T]; +type Tuple2 = [a: T, b: T]; +type Tuple3 = [a: T, T]; // Named Tuple plus Anonymous Tuple +``` + +## Fixed Length Tuple + +A Fixed Length Tuple is a specific type of tuple that enforces a fixed number of elements of specific types, and disallows any modifications to the length of the tuple once it is defined. + +Fixed Length Tuples are useful when you need to represent a collection of values with a specific number of elements and specific types, and you want to ensure that the length and types of the tuple cannot be changed inadvertently. + + +```typescript +const x = [10, 'hello'] as const; +x.push(2); // Error +``` + +## Union Type + +A Union Type is a type that represents a value that can be one of several types. Union Types are denoted using the `|` symbol between each possible type. + +```typescript +let x: string | number; +x = 'hello'; // Valid +x = 123; // Valid +``` + +## Intersection Types + +An Intersection Type is a type that represents a value that has all the properties of two or more types. Intersection Types are denoted using the `&` symbol between each type. + +```typescript +type X = { + a: string; +}; + +type Y = { + b: string; +}; + +type J = X & Y; // Intersection + +const j: J = { + a: 'a', + b: 'b', +}; +``` + +## Type Indexing + +Type indexing refers to the ability to define types that can be indexed by a key that is not known in advance, using an index signature to specify the type for properties that are not explicitly declared. + +```typescript +type Dictionary = { + [key: string]: T; +}; +const myDict: Dictionary = { a: 'a', b: 'b' }; +console.log(myDict['a']); // Returns a +``` + +## Type from Value + +Type from Value in TypeScript refers to the automatic inference of a type from a value or expression through type inference. + +```typescript +const x = 'x'; // TypeScript infers 'x' as a string literal with 'const' (immutable), but widens it to 'string' with 'let' (reassignable). +``` + +## Type from Func Return + +Type from Func Return refers to the ability to automatically infer the return type of a function based on its implementation. This allows TypeScript to determine the type of the value returned by the function without explicit type annotations. + +```typescript +const add = (x: number, y: number) => x + y; // TypeScript can infer that the return type of the function is a number +``` + +## Type from Module + +Type from Module refers to the ability to use a module's exported values to automatically infer their types. When a module exports a value with a specific type, TypeScript can use that information to automatically infer the type of that value when it is imported into another module. + + +```typescript +// calc.ts +export const add = (x: number, y: number) => x + y; +// index.ts +import { add } from 'calc'; +const r = add(1, 2); // r is number +``` + +## Mapped Types + +Mapped Types in TypeScript allow you to create new types based on an existing type by transforming each property using a mapping function. By mapping existing types, you can create new types that represent the same information in a different format. To create a mapped type, you access the properties of an existing type using the `keyof` operator and then alter them to produce a new type. +In the following example: + +```typescript +type MyMappedType = { + [P in keyof T]: T[P][]; +}; +type MyType = { + foo: string; + bar: number; +}; +type MyNewType = MyMappedType; +const x: MyNewType = { + foo: ['hello', 'world'], + bar: [1, 2, 3], +}; +``` + +we define MyMappedType to map over T's properties, creating a new type with each property as an array of its original type. Using this, we create MyNewType to represent the same info as MyType, but with each property as an array. + +## Mapped Type Modifiers + +Mapped Type Modifiers in TypeScript enable the transformation of properties within an existing type: + +* `readonly` or `+readonly`: This renders a property in the mapped type as read-only. +* `-readonly`: This allows a property in the mapped type to be mutable. +* `?`: This designates a property in the mapped type as optional. + +Examples: + +```typescript +type ReadOnly = { readonly [P in keyof T]: T[P] }; // All properties marked as read-only + +type Mutable = { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable + +type MyPartial = { [P in keyof T]?: T[P] }; // All properties marked as optional +``` + +## Conditional Types + +Conditional Types are a way to create a type that depends on a condition, where the type to be created is determined based on the result of the condition. They are defined using the `extends` keyword and a ternary operator to conditionally choose between two types. + +```typescript +type IsArray = T extends any[] ? true : false; + +const myArray = [1, 2, 3]; +const myNumber = 42; + +type IsMyArrayAnArray = IsArray; // Type true +type IsMyNumberAnArray = IsArray; // Type false +``` + +## Distributive Conditional Types + +Distributive Conditional Types are a feature that allow a type to be distributed over a union of types, by applying a transformation to each member of the union individually. +This can be especially useful when working with mapped types or higher-order types. + +```typescript +type Nullable = T extends any ? T | null : never; +type NumberOrBool = number | boolean; +type NullableNumberOrBool = Nullable; // number | boolean | null +``` + +## infer Type Inference in Conditional Types + +The `infer`keyword is used in conditional types to infer (extract) the type of a generic parameter from a type that depends on it. This allows you to write more flexible and reusable type definitions. + +```typescript +type ElementType = T extends (infer U)[] ? U : never; +type Numbers = ElementType; // number +type Strings = ElementType; // string +``` + +## Predefined Conditional Types + +In TypeScript, Predefined Conditional Types are built-in conditional types provided by the language. They are designed to perform common type transformations based on the characteristics of a given type. + +`Exclude`: This type removes all the types from Type that are assignable to ExcludedType. + +`Extract`: This type extracts all the types from Union that are assignable to Type. + +`NonNullable`: This type removes null and undefined from Type. + +`ReturnType`: This type extracts the return type of a function Type. + +`Parameters`: This type extracts the parameter types of a function Type. + +`Required`: This type makes all properties in Type required. + +`Partial`: This type makes all properties in Type optional. + +`Readonly`: This type makes all properties in Type readonly. + +## Template Union Types + +Template union types can be used to merge and manipulate text inside the type system for instance: + +```typescript +type Status = 'active' | 'inactive'; +type Products = 'p1' | 'p2'; +type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" +``` + +## Any type + +The `any` type is a special type (universal supertype) that can be used to represent any type of value (primitives, objects, arrays, functions, errors, symbols). It is often used in situations where the type of a value is not known at compile time, or when working with values from external APIs or libraries that do not have TypeScript typings. + +By utilizing `any` type, you are indicating to the TypeScript compiler that values should be represented without any limitations. In order to maximizing type safety in your code consider the following: + +* Limit the usage of `any` to specific cases where the type is truly unknown. +* Do not return `any` types from a function as you will lose type safety in the code using that function weakening your type safety. +* Instead of `any` use `@ts-ignore` if you need to silence the compiler. + +```typescript +let value: any; +value = true; // Valid +value = 7; // Valid +``` + +## Unknown type + +In TypeScript, the `unknown` type represents a value that is of an unknown type. Unlike `any` type, which allows for any type of value, `unknown` requires a type check or assertion before it can be used in a specific way so no operations are permitted on an `unknown` without first asserting or narrowing to a more specific type. + +The `unknown` type is only assignable to any type and the `unknown` type itself, it is a type-safe alternative to `any`. + + +```typescript +let value: unknown; + +let value1: unknown = value; // Valid +let value2: any = value; // Valid +let value3: boolean = value; // Invalid +let value4: number = value; // Invalid +``` + +```typescript +const add = (a: unknown, b: unknown): number | undefined => + typeof a === 'number' && typeof b === 'number' ? a + b : undefined; +console.log(add(1, 2)); // 3 +console.log(add('x', 2)); // undefined +``` + +## Void type + +The `void` type is used to indicate that a function does not return a value. + +```typescript +const sayHello = (): void => { + console.log('Hello!'); +}; +``` + +## Never type + +The `never` type represents values that never occur. It is used to denote functions or expressions that never return or throw an error. + +For instance an infinite loop: + +```typescript +const infiniteLoop = (): never => { + while (true) { + // do something + } +}; +``` + +Throwing an error: + +```typescript +const throwError = (message: string): never => { + throw new Error(message); +}; +``` + +The `never` type is useful in ensuring type safety and catching potential errors in your code. It helps TypeScript analyze and infer more precise types when used in combination with other types and control flow statements, for instance: + +```typescript +type Direction = 'up' | 'down'; +const move = (direction: Direction): void => { + switch (direction) { + case 'up': + // move up + break; + case 'down': + // move down + break; + default: + const exhaustiveCheck: never = direction; + throw new Error(`Unhandled direction: ${exhaustiveCheck}`); + } +}; +``` + +## Interface and Type + +### Common Syntax + +In TypeScript, interfaces define the structure of objects, specifying the names and types of properties or methods that an object must have. The common syntax for defining an interface in TypeScript is as follows: + + +```typescript +interface InterfaceName { + property1: Type1; + // ... + method1(arg1: ArgType1, arg2: ArgType2): ReturnType; + // ... +} +``` + +Similarly for type definition: + + +```typescript +type TypeName = { + property1: Type1; + // ... + method1(arg1: ArgType1, arg2: ArgType2): ReturnType; + // ... +}; +``` + +`interface InterfaceName` or `type TypeName`: Defines the name of the interface. +`property1`: `Type1`: Specifies the properties of the interface along with their corresponding types. Multiple properties can be defined, each separated by a semicolon. +`method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specifies the methods of the interface. Methods are defined with their names, followed by a parameter list in parentheses and the return type. Multiple methods can be defined, each separated by a semicolon. + +Example interface: + +```typescript +interface Person { + name: string; + age: number; + greet(): void; +} +``` + +Example of type: + +```typescript +type TypeName = { + property1: string; + method1(arg1: string, arg2: string): string; +}; +``` + +In TypeScript, types are used to define the shape of data and enforce type checking. There are several common syntaxes for defining types in TypeScript, depending on the specific use case. Here are some examples: + +### Basic Types + +```typescript +let myNumber: number = 123; // number type +let myBoolean: boolean = true; // boolean type +let myArray: string[] = ['a', 'b']; // array of strings +let myTuple: [string, number] = ['a', 123]; // tuple +``` + +### Objects and Interfaces + +```typescript +const x: { name: string; age: number } = { name: 'Simon', age: 7 }; +``` + +### Union and Intersection Types + +```typescript +type MyType = string | number; // Union type +let myUnion: MyType = 'hello'; // Can be a string +myUnion = 123; // Or a number + +type TypeA = { name: string }; +type TypeB = { age: number }; +type CombinedType = TypeA & TypeB; // Intersection type +let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with both name and age properties +``` + +## Built-in Type Primitives + +TypeScript has several built-in type primitives that can be used to define variables, function parameters, and return types: + +* `number`: Represents numeric values, including integers and floating-point numbers. +* `string`: Represents textual data +* `boolean`: Represents logical values, which can be either true or false. +* `null`: Represents the absence of a value. +* `undefined`: Represents a value that has not been assigned or has not been defined. +* `symbol`: Represents a unique identifier. Symbols are typically used as keys for object properties. +* `bigint`: Represents arbitrary-precision integers. +* `any`: Represents a dynamic or unknown type. Variables of type any can hold values of any type, and they bypass type checking. +* `void`: Represents the absence of any type. It is commonly used as the return type of functions that do not return a value. +* `never`: Represents a type for values that never occur. It is typically used as the return type of functions that throw an error or enter an infinite loop. + +## Common Built-in JS Objects + +TypeScript is a superset of JavaScript, it includes all the commonly used built-in JavaScript objects. You can find an extensive list of these objects on the Mozilla Developer Network (MDN) documentation website: +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) + +Here is a list of some commonly used built-in JavaScript objects: + +* Function +* Object +* Boolean +* Error +* Number +* BigInt +* Math +* Date +* String +* RegExp +* Array +* Map +* Set +* Promise +* Intl + +## Overloads + +Function overloads in TypeScript allow you to define multiple function signatures for a single function name, enabling you to define functions that can be called in multiple ways. Here's an example: + +```typescript +// Overloads +function sayHi(name: string): string; +function sayHi(names: string[]): string[]; + +// Implementation +function sayHi(name: unknown): unknown { + if (typeof name === 'string') { + return `Hi, ${name}!`; + } else if (Array.isArray(name)) { + return name.map(name => `Hi, ${name}!`); + } + throw new Error('Invalid value'); +} + +sayHi('xx'); // Valid +sayHi(['aa', 'bb']); // Valid +``` + +Here's another example of using function overloads within a `class`: + +```typescript +class Greeter { + message: string; + + constructor(message: string) { + this.message = message; + } + + // overload + sayHi(name: string): string; + sayHi(names: string[]): ReadonlyArray; + + // implementation + sayHi(name: unknown): unknown { + if (typeof name === 'string') { + return `${this.message}, ${name}!`; + } else if (Array.isArray(name)) { + return name.map(name => `${this.message}, ${name}!`); + } + throw new Error('value is invalid'); + } +} +console.log(new Greeter('Hello').sayHi('Simon')); +``` + +## Merging and Extension + +Merging and extension refer to two different concepts related to working with types and interfaces. + +Merging allows you to combine multiple declarations of the same name into a single definition, for example, when you define an interface with the same name multiple times: + +```typescript +interface X { + a: string; +} + +interface X { + b: number; +} + +const person: X = { + a: 'a', + b: 7, +}; +``` + +Extension refers to the ability to extend or inherit from existing types or interfaces to create new ones. It is a mechanism to add additional properties or methods to an existing type without modifying its original definition. Example: + +```typescript +interface Animal { + name: string; + eat(): void; +} + +interface Bird extends Animal { + sing(): void; +} + +const dog: Bird = { + name: 'Bird 1', + eat() { + console.log('Eating'); + }, + sing() { + console.log('Singing'); + }, +}; +``` + +## Differences between Type and Interface + +Declaration merging (augmentation): + +Interfaces support declaration merging, which means that you can define multiple interfaces with the same name, and TypeScript will merge them into a single interface with the combined properties and methods. On the other hand, types do not support declaration merging. This can be helpful when you want to add extra functionality or customize existing types without modifying the original definitions or patching missing or incorrect types. + +```typescript +interface A { + x: string; +} +interface A { + y: string; +} +const j: A = { + x: 'xx', + y: 'yy', +}; +``` + +Extending other types/interfaces: + +Both types and interfaces can extend other types/interfaces, but the syntax is different. With interfaces, you use the `extends` keyword to inherit properties and methods from other interfaces. However, an interface cannot extend a complex type like a union type. + +```typescript +interface A { + x: string; + y: number; +} +interface B extends A { + z: string; +} +const car: B = { + x: 'x', + y: 123, + z: 'z', +}; +``` + +For types, you use the & operator to combine multiple types into a single type (intersection). + +```typescript +interface A { + x: string; + y: number; +} + +type B = A & { + j: string; +}; + +const c: B = { + x: 'x', + y: 123, + j: 'j', +}; +``` + +Union and Intersection Types: + +Types are more flexible when it comes to defining Union and Intersection Types. With the `type` keyword, you can easily create union types using the `|` operator and intersection types using the `&` operator. While interfaces can also represent union types indirectly, they don't have built-in support for intersection types. + +```typescript +type Department = 'dep-x' | 'dep-y'; // Union + +type Person = { + name: string; + age: number; +}; + +type Employee = { + id: number; + department: Department; +}; + +type EmployeeInfo = Person & Employee; // Intersection +``` + +Example with interfaces: + +```typescript +interface A { + x: 'x'; +} +interface B { + y: 'y'; +} + +type C = A | B; // Union of interfaces +``` + +## Клас (Class) + +### Агульны сінтаксіс класа + +Ключавое слова `class` выкарыстоўваецца ў TypeScript для вызначэння класа. Ніжэй вы можаце ўбачыць прыклад: + +```typescript +class Person { + private name: string; + private age: number; + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + public sayHi(): void { + console.log( + `Hello, my name is ${this.name} and I am ${this.age} years old.` + ); + } +} +``` + +Ключавое слова `class` выкарыстоўваецца для вызначэння класа з імем "Person". + +Клас мае дзве прыватныя ўласцівасці: name тыпу `string` і age тыпу `number`. + +Канструктар вызначаецца з дапамогай ключавога слова `constructor`. Ён прымае name і age ў якасці параметраў і прысвойвае іх адпаведным уласцівасцям. + +Клас мае `public` (публічны) метад з імем sayHi, які выводзіць прывітальнае паведамленне. + +Каб стварыць асобнік класа ў TypeScript, вы можаце выкарыстаць ключавое слова `new`, за якім ідзе імя класа і дужкі `()`. Напрыклад: + + +```typescript +const myObject = new Person('John Doe', 25); +myObject.sayHi(); // Output: Hello, my name is John Doe and I am 25 years old. +``` + +### Канструктар (Constructor) + +Канструктары — гэта спецыяльныя метады ўнутры класа, якія выкарыстоўваюцца для ініцыялізацыі ўласцівасцей аб'екта пры стварэнні асобніка класа. + +```typescript +class Person { + public name: string; + public age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + + sayHello() { + console.log( + `Hello, my name is ${this.name} and I'm ${this.age} years old.` + ); + } +} + +const john = new Person('Simon', 17); +john.sayHello(); +``` + +Можна перагрузіць (overload) канструктар, выкарыстоўваючы наступны сінтаксіс: + +```typescript +type Sex = 'm' | 'f'; + +class Person { + name: string; + age: number; + sex: Sex; + + constructor(name: string, age: number, sex?: Sex); + constructor(name: string, age: number, sex: Sex) { + this.name = name; + this.age = age; + this.sex = sex ?? 'm'; + } +} + +const p1 = new Person('Simon', 17); +const p2 = new Person('Alice', 22, 'f'); +``` + +У TypeScript можна вызначыць некалькі перагрузак канструктара, але вы можаце мець толькі адну рэалізацыю, якая павінна быць сумяшчальная з усімі перагрузкамі; гэтага можна дасягнуць, выкарыстоўваючы неабавязковы параметр. + +```typescript +class Person { + name: string; + age: number; + + constructor(); + constructor(name: string); + constructor(name: string, age: number); + constructor(name?: string, age?: number) { + this.name = name ?? 'Unknown'; + this.age = age ?? 0; + } + + displayInfo() { + console.log(`Name: ${this.name}, Age: ${this.age}`); + } +} + +const person1 = new Person(); +person1.displayInfo(); // Name: Unknown, Age: 0 + +const person2 = new Person('John'); +person2.displayInfo(); // Name: John, Age: 0 + +const person3 = new Person('Jane', 25); +person3.displayInfo(); // Name: Jane, Age: 25 +``` + +### Прыватныя і абароненыя канструктары + +У TypeScript канструктары могуць быць пазначаны як прыватныя (`private`) або абароненыя (`protected`), што абмяжоўвае іх даступнасць і выкарыстанне. + +Прыватныя канструктары: +Могуць быць выкліканы толькі ўнутры самога класа. Прыватныя канструктары часта выкарыстоўваюцца ў сцэнарыях, калі вы хочаце прымусова выкарыстоўваць шаблон адзіночкі (singleton) або абмежаваць стварэнне асобнікаў фабрычным метадам унутры класа. + +Абароненыя канструктары: +Абароненыя канструктары карысныя, калі вы хочаце стварыць базавы клас, асобнікі якога не павінны стварацца напрамую, але які можа быць пашыраны падкласамі. + +```typescript +class BaseClass { + protected constructor() {} +} + +class DerivedClass extends BaseClass { + private value: number; + + constructor(value: number) { + super(); + this.value = value; + } +} + +// Attempting to instantiate the base class directly will result in an error +// const baseObj = new BaseClass(); // Error: Constructor of class 'BaseClass' is protected. + +// Create an instance of the derived class +const derivedObj = new DerivedClass(10); +``` + +### Мадыфікатары доступу + +Мадыфікатары доступу `private`, `protected` і `public` выкарыстоўваюцца для кіравання бачнасцю і даступнасцю членаў класа, такіх як уласцівасці і метады, у класах TypeScript. Гэтыя мадыфікатары неабходныя для забеспячэння інкапсуляцыі і ўсталявання меж для доступу і змены ўнутранага стану класа. + +Мадыфікатар `private` (прыватны) абмяжоўвае доступ да члена класа толькі ў межах класа, які яго змяшчае. + +Мадыфікатар `protected` (абаронены) дазваляе доступ да члена класа ў межах класа, які яго змяшчае, і яго вытворных класаў. + +Мадыфікатар `public` (публічны) забяспечвае неабмежаваны доступ да члена класа, дазваляючы звяртацца да яго адкуль заўгодна. + +### Get і Set (Гетэры і Сэтэры) + +Гетэры (Getters) і сэтэры (setters) — гэта спецыяльныя метады, якія дазваляюць вызначаць карыстальніцкія паводзіны доступу і мадыфікацыі для ўласцівасцей класа. Яны дазваляюць інкапсуляваць унутраны стан аб'екта і забяспечваць дадатковую логіку пры атрыманні або ўсталяванні значэнняў уласцівасцей. +У TypeScript гетэры і сэтэры вызначаюцца з дапамогай ключавых слоў `get` і `set` адпаведна. Вось прыклад: + +```typescript +class MyClass { + private _myProperty: string; + + constructor(value: string) { + this._myProperty = value; + } + get myProperty(): string { + return this._myProperty; + } + set myProperty(value: string) { + this._myProperty = value; + } +} +``` + +### Аўтаматычныя аксэсары ў класах + +TypeScript версіі 4.9 дадае падтрымку аўтаматычных аксэсараў (auto-accessors), будучай функцыі ECMAScript. Яны нагадваюць уласцівасці класа, але аб'яўляюцца з дапамогай ключавога слова «accessor». + +```typescript +class Animal { + accessor name: string; + + constructor(name: string) { + this.name = name; + } +} +``` + +Аўтаматычныя аксэсары ператвараюцца ("de-sugared") у прыватныя аксэсары `get` і `set`, якія працуюць з недаступнай уласцівасцю. + + +```typescript +class Animal { + #__name: string; + + get name() { + return this.#__name; + } + set name(value: string) { + this.#__name = name; + } + + constructor(name: string) { + this.name = name; + } +} +``` + +### this + +У TypeScript ключавое слова `this` спасылаецца на бягучы асобнік класа ў яго метадах або канструктарах. Гэта дазваляе атрымліваць доступ і змяняць уласцівасці і метады класа з яго ўласнай вобласці бачнасці. +Яно забяспечвае спосаб доступу і маніпулявання ўнутраным станам аб'екта ў яго ўласных метадах. + +```typescript +class Person { + private name: string; + constructor(name: string) { + this.name = name; + } + public introduce(): void { + console.log(`Hello, my name is ${this.name}.`); + } +} + +const person1 = new Person('Alice'); +person1.introduce(); // Hello, my name is Alice. +``` + +### Уласцівасці параметраў + +Уласцівасці параметраў дазваляюць аб'яўляць і ініцыялізаваць уласцівасці класа непасрэдна ў параметрах канструктара, пазбягаючы шаблоннага кода, напрыклад: + +```typescript +class Person { + constructor( + private name: string, + public age: number + ) { + // Ключавыя словы "private" і "public" у канструктары + // аўтаматычна аб'яўляюць і ініцыялізуюць адпаведныя ўласцівасці класа. + } + public introduce(): void { + console.log( + `Hello, my name is ${this.name} and I am ${this.age} years old.` + ); + } +} +const person = new Person('Alice', 25); +person.introduce(); +``` + +### Абстрактныя класы + +Абстрактныя класы выкарыстоўваюцца ў TypeScript галоўным чынам для атрымання ў спадчыну (inheritance); яны забяспечваюць спосаб вызначэння агульных уласцівасцей і метадаў, якія могуць быць успадкаваны падкласамі. +Гэта карысна, калі вы хочаце вызначыць агульныя паводзіны і прымусіць падкласы рэалізаваць пэўныя метады. Яны забяспечваюць спосаб стварэння іерархіі класаў, дзе абстрактны базавы клас забяспечвае агульны інтэрфейс і агульную функцыянальнасць для падкласаў. + +```typescript +abstract class Animal { + protected name: string; + + constructor(name: string) { + this.name = name; + } + + abstract makeSound(): void; +} + +class Cat extends Animal { + makeSound(): void { + console.log(`${this.name} meows.`); + } +} + +const cat = new Cat('Whiskers'); +cat.makeSound(); // Output: Whiskers meows. +``` + +### З абагульненымі тыпамі (Generics) + +Класы з абагульненымі тыпамі (generics) дазваляюць вызначаць шматразовыя класы, якія могуць працаваць з рознымі тыпамі. + +```typescript +class Container { + private item: T; + + constructor(item: T) { + this.item = item; + } + + getItem(): T { + return this.item; + } + + setItem(item: T): void { + this.item = item; + } +} + +const container1 = new Container(42); +console.log(container1.getItem()); // 42 + +const container2 = new Container('Hello'); +container2.setItem('World'); +console.log(container2.getItem()); // World +``` + +### Дэкаратары + +Дэкаратары забяспечваюць механізм дадання метаданых, змены паводзін, праверкі або пашырэння функцыянальнасці мэтавага элемента. Гэта функцыі, якія выконваюцца падчас выканання (at runtime). Да аб'яўлення можна прымяніць некалькі дэкаратараў. + +Дэкаратары з'яўляюцца эксперыментальнымі функцыямі, і наступныя прыклады сумяшчальныя толькі з версіяй TypeScript 5 або вышэй з выкарыстаннем ES6. + +Для версій TypeScript ранейшых за 5 яны павінны быць уключаны з дапамогай уласцівасці `experimentalDecorators` у вашым `tsconfig.json` або з дапамогай `--experimentalDecorators` у вашым камандным радку (але наступны прыклад працаваць не будзе). + +Некаторыя з распаўсюджаных выпадкаў выкарыстання дэкаратараў ўключаюць: + +* Назіранне за зменамі ўласцівасцей. +* Назіранне за выклікамі метадаў. +* Даданне дадатковых уласцівасцей або метадаў. +* Валідацыя падчас выканання. +* Аўтаматычная серыялізацыя і дэсерыялізацыя. +* Лагіраванне. +* Аўтарызацыя і аўтэнтыфікацыя. +* Ахова ад памылак (Error guarding). + +Заўвага: Дэкаратары для версіі 5 не дазваляюць дэкараваць параметры. + +Тыпы дэкаратараў: + +#### Дэкаратары класа + +Дэкаратары класа карысныя для пашырэння існуючага класа, напрыклад, дадання ўласцівасцей або метадаў, або збору асобнікаў класа. У наступным прыкладзе мы дадаем метад `toString`, які пераўтварае клас у радковыя прадстаўленне. + +```typescript +type Constructor = new (...args: any[]) => T; + +function toString( + Value: Class, + context: ClassDecoratorContext +) { + return class extends Value { + constructor(...args: any[]) { + super(...args); + console.log(JSON.stringify(this)); + console.log(JSON.stringify(context)); + } + }; +} + +@toString +class Person { + name: string; + + constructor(name: string) { + this.name = name; + } + + greet() { + return 'Hello, ' + this.name; + } +} +const person = new Person('Simon'); +/* Logs: +{"name":"Simon"} +{"kind":"class","name":"Person"} +*/ +``` + +#### Дэкаратар уласцівасці + +Дэкаратары ўласцівасці карысныя для змены паводзін уласцівасці, напрыклад, для змены значэнняў ініцыялізацыі. У наступным кодзе ў нас ёсць скрыпт, які ўсталёўвае ўласцівасць заўсёды ў верхнім рэгістры: + +```typescript +function upperCase( + target: undefined, + context: ClassFieldDecoratorContext +) { + return function (this: T, value: string) { + return value.toUpperCase(); + }; +} + +class MyClass { + @upperCase + prop1 = 'hello!'; +} + +console.log(new MyClass().prop1); // Logs: HELLO! +``` + +#### Дэкаратар метаду + +Дэкаратары метаду дазваляюць змяняць або паляпшаць паводзіны метадаў. Ніжэй прыведзены прыклад простага лагера (logger): + +```typescript +function log( + target: (this: This, ...args: Args) => Return, + context: ClassMethodDecoratorContext< + This, + (this: This, ...args: Args) => Return + > +) { + const methodName = String(context.name); + + function replacementMethod(this: This, ...args: Args): Return { + console.log(`LOG: Entering method '${methodName}'.`); + const result = target.call(this, ...args); + console.log(`LOG: Exiting method '${methodName}'.`); + return result; + } + + return replacementMethod; +} + +class MyClass { + @log + sayHello() { + console.log('Hello!'); + } +} + +new MyClass().sayHello(); +``` + +Гэта выводзіць у журнал: + +```shell +LOG: Entering method 'sayHello'. +Hello! +LOG: Exiting method 'sayHello'. +``` + +#### Дэкаратары гетэра і сэтэра + +Дэкаратары гетэра і сэтэра дазваляюць змяняць або паляпшаць паводзіны аксэсараў класа. Яны карысныя, напрыклад, для праверкі прысваенняў уласцівасцей. Вось просты прыклад для дэкаратара гетэра: + +```typescript +function range(min: number, max: number) { + return function ( + target: (this: This) => Return, + context: ClassGetterDecoratorContext + ) { + return function (this: This): Return { + const value = target.call(this); + if (value < min || value > max) { + throw 'Invalid'; + } + Object.defineProperty(this, context.name, { + value, + enumerable: true, + }); + return value; + }; + }; +} + +class MyClass { + private _value = 0; + + constructor(value: number) { + this._value = value; + } + @range(1, 100) + get getValue(): number { + return this._value; + } +} + +const obj = new MyClass(10); +console.log(obj.getValue); // Valid: 10 + +const obj2 = new MyClass(999); +console.log(obj2.getValue); // Throw: Invalid! +``` + +#### Метаданыя дэкаратара + +Метаданыя дэкаратара спрашчаюць працэс прымянення і выкарыстання метаданых дэкаратарамі ў любым класе. Яны могуць атрымаць доступ да новай уласцівасці `metadata` у аб'екце кантэксту, якая можа служыць ключом як для прымітываў, так і для аб'ектаў. +Інфармацыя пра метаданыя можа быць даступная ў класе праз `Symbol.metadata`. + +Метаданыя могуць выкарыстоўвацца для розных мэтаў, такіх як адладка, серыялізацыя або ўкараненне залежнасцей (dependency injection) з дапамогай дэкаратараў. + +```typescript +//@ts-ignore +Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polify + +type Context = + | ClassFieldDecoratorContext + | ClassAccessorDecoratorContext + | ClassMethodDecoratorContext; // Context contains property metadata: DecoratorMetadata + +function setMetadata(_target: any, context: Context) { + // Set the metadata object with a primitive value + context.metadata[context.name] = true; +} + +class MyClass { + @setMetadata + a = 123; + + @setMetadata + accessor b = 'b'; + + @setMetadata + fn() {} +} + +const metadata = MyClass[Symbol.metadata]; // Get metadata information + +console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} +``` + +### Inheritance + +Inheritance refers to the mechanism by which a class can inherit properties and methods from another class, known as the base class or superclass. The derived class, also called the child class or subclass, can extend and specialize the functionality of the base class by adding new properties and methods or overriding existing ones. + +```typescript +class Animal { + name: string; + + constructor(name: string) { + this.name = name; + } + + speak(): void { + console.log('The animal makes a sound'); + } +} + +class Dog extends Animal { + breed: string; + + constructor(name: string, breed: string) { + super(name); + this.breed = breed; + } + + speak(): void { + console.log('Woof! Woof!'); + } +} + +// Create an instance of the base class +const animal = new Animal('Generic Animal'); +animal.speak(); // The animal makes a sound + +// Create an instance of the derived class +const dog = new Dog('Max', 'Labrador'); +dog.speak(); // Woof! Woof!" +``` + +TypeScript does not support multiple inheritance in the traditional sense and instead allows inheritance from a single base class. +TypeScript supports multiple interfaces. An interface can define a contract for the structure of an object, and a class can implement multiple interfaces. This allows a class to inherit behavior and structure from multiple sources. + +```typescript +interface Flyable { + fly(): void; +} + +interface Swimmable { + swim(): void; +} + +class FlyingFish implements Flyable, Swimmable { + fly() { + console.log('Flying...'); + } + + swim() { + console.log('Swimming...'); + } +} + +const flyingFish = new FlyingFish(); +flyingFish.fly(); +flyingFish.swim(); +``` + +The `class` keyword in TypeScript, similar to JavaScript, is often referred to as syntactic sugar. It was introduced in ECMAScript 2015 (ES6) to offer a more familiar syntax for creating and working with objects in a class-based manner. However, it's important to note that TypeScript, being a superset of JavaScript, ultimately compiles down to JavaScript, which remains prototype-based at its core. + +### Statics + +TypeScript has static members. To access the static members of a class, you can use the class name followed by a dot, without the need to create an object. + +```typescript +class OfficeWorker { + static memberCount: number = 0; + + constructor(private name: string) { + OfficeWorker.memberCount++; + } +} + +const w1 = new OfficeWorker('James'); +const w2 = new OfficeWorker('Simon'); +const total = OfficeWorker.memberCount; +console.log(total); // 2 +``` + +### Property initialization + +There are several ways how you can initialize properties for a class in TypeScript: + +Inline: + +In the following example these initial values will be used when an instance of the class is created. + +```typescript +class MyClass { + property1: string = 'default value'; + property2: number = 42; +} +``` + +In the constructor: + +```typescript +class MyClass { + property1: string; + property2: number; + + constructor() { + this.property1 = 'default value'; + this.property2 = 42; + } +} +``` + +Using constructor parameters: + +```typescript +class MyClass { + constructor( + private property1: string = 'default value', + public property2: number = 42 + ) { + // There is no need to assign the values to the properties explicitly. + } + log() { + console.log(this.property2); + } +} +const x = new MyClass(); +x.log(); +``` + +### Перагрузка метадаў + +Перагрузка метадаў дазваляе класу мець некалькі метадаў з адным і тым жа імем, але рознымі тыпамі параметраў або рознай колькасцю параметраў. Гэта дазваляе нам выклікаць метад рознымі спосабамі ў залежнасці ад перададзеных аргументаў. + +```typescript +class MyClass { + add(a: number, b: number): number; // Overload signature 1 + add(a: string, b: string): string; // Overload signature 2 + + add(a: number | string, b: number | string): number | string { + if (typeof a === 'number' && typeof b === 'number') { + return a + b; + } + if (typeof a === 'string' && typeof b === 'string') { + return a.concat(b); + } + throw new Error('Invalid arguments'); + } +} + +const r = new MyClass(); +console.log(r.add(10, 5)); // Logs 15 +``` + +## Абагульненыя тыпы (Generics) + +Абагульненыя тыпы (Generics) дазваляюць ствараць шматразовыя кампаненты і функцыі, якія могуць працаваць з некалькімі тыпамі. З дапамогай абагульненых тыпаў вы можаце параметрызаваць тыпы, функцыі і інтэрфейсы, дазваляючы ім працаваць з рознымі тыпамі без папярэдняга відавочнага ўказання. + +Абагульненыя тыпы дазваляюць зрабіць код больш гнуткім і прыдатным для паўторнага выкарыстання. + +### Абагульнены тып + +Каб вызначыць абагульнены тып, вы выкарыстоўваеце вуглавыя дужкі (`<>`) для ўказання параметраў тыпу, напрыклад: + +```typescript +function identity(arg: T): T { + return arg; +} +const a = identity('x'); +const b = identity(123); + +const getLen = (data: ReadonlyArray) => data.length; +const len = getLen([1, 2, 3]); +``` + +### Абагульненыя класы + +Абагульненыя тыпы таксама могуць прымяняцца да класаў, такім чынам яны могуць працаваць з некалькімі тыпамі, выкарыстоўваючы параметры тыпу. Гэта карысна для стварэння шматразовых азначэнняў класаў, якія могуць працаваць з рознымі тыпамі даных, захоўваючы пры гэтым бяспеку тыпаў. + +```typescript +class Container { + private item: T; + + constructor(item: T) { + this.item = item; + } + + getItem(): T { + return this.item; + } +} + +const numberContainer = new Container(123); +console.log(numberContainer.getItem()); // 123 + +const stringContainer = new Container('hello'); +console.log(stringContainer.getItem()); // hello +``` + +### Абмежаванні абагульненых тыпаў (Generic Constraints) + +Абагульненыя параметры могуць быць абмежаваныя з выкарыстаннем ключавога слова `extends`, за якім ідзе тып або інтэрфейс, якому павінен задавальняць параметр тыпу. + +У наступным прыкладзе T павінен утрымліваць уласцівасць `length`, каб быць сапраўдным: + + +```typescript +const printLen = (value: T): void => { + console.log(value.length); +}; + +printLen('Hello'); // 5 +printLen([1, 2, 3]); // 3 +printLen({ length: 10 }); // 10 +printLen(123); // Invalid +``` + +Цікавай асаблівасцю абагульненых тыпаў, уведзенай у версіі 3.4 RC, з'яўляецца вывад тыпу функцыі вышэйшага парадку, які ўвёў распаўсюджванне аргументаў абагульненага тыпу: + +```typescript +declare function pipe( + ab: (...args: A) => B, + bc: (b: B) => C +): (...args: A) => C; + +declare function list(a: T): T[]; +declare function box(x: V): { value: V }; + +const listBox = pipe(list, box); // (a: T) => { value: T[] } +const boxList = pipe(box, list); // (x: V) => { value: V }[] +``` + +Гэтая функцыянальнасць дазваляе лягчэй пісаць тыпабяспечны код у стылі "pointfree", што распаўсюджана ў функцыянальным праграмаванні. + +### Абагульненае кантэкстнае звужэнне + +Кантэкстнае звужэнне для абагульненых тыпаў — гэта механізм у TypeScript, які дазваляе кампілятару звузіць тып абагульненага параметра на аснове кантэксту, у якім ён выкарыстоўваецца; гэта карысна пры працы з абагульненымі тыпамі ва ўмоўных аператарах: + +```typescript +function process(value: T): void { + if (typeof value === 'string') { + // Value is narrowed down to type 'string' + console.log(value.length); + } else if (typeof value === 'number') { + // Value is narrowed down to type 'number' + console.log(value.toFixed(2)); + } +} + +process('hello'); // 5 +process(3.14159); // 3.14 +``` + +## Сцёртыя структурныя тыпы + +У TypeScript аб'екты не павінны адпавядаць канкрэтнаму, дакладнаму тыпу. Напрыклад, калі мы ствараем аб'ект, які задавальняе патрабаванням інтэрфейсу, мы можам выкарыстоўваць гэты аб'ект у месцах, дзе патрабуецца гэты інтэрфейс, нават калі паміж імі не было відавочнай сувязі. +Прыклад: + +```typescript +type NameProp1 = { + prop1: string; +}; + +function log(x: NameProp1) { + console.log(x.prop1); +} + +const obj = { + prop2: 123, + prop1: 'Origin', +}; + +log(obj); // Valid +``` + +## Прасторы імёнаў (Namespacing) + +У TypeScript прасторы імёнаў (namespaces) выкарыстоўваюцца для арганізацыі кода ў лагічныя кантэйнеры, прадухіляючы калізіі імёнаў і забяспечваючы спосаб групоўкі звязанага кода разам. +Выкарыстанне ключавога слова `export` дазваляе атрымаць доступ да прасторы імёнаў у «вонкавых» модулях. + +```typescript +export namespace MyNamespace { + export interface MyInterface1 { + prop1: boolean; + } + export interface MyInterface2 { + prop2: string; + } +} + +const a: MyNamespace.MyInterface1 = { + prop1: true, +}; +``` + +## Сімвалы (Symbols) + +Сімвалы (Symbols) — гэта прымітыўны тып даных, які прадстаўляе нязменнае значэнне, якое гарантавана будзе глабальна ўнікальным на працягу ўсяго часу выканання праграмы. + +Сімвалы могуць выкарыстоўвацца ў якасці ключоў уласцівасцей аб'екта і забяспечваюць спосаб стварэння ўласцівасцей, якія не падлягаюць пералічэнню. + +```typescript +const key1: symbol = Symbol('key1'); +const key2: symbol = Symbol('key2'); + +const obj = { + [key1]: 'value 1', + [key2]: 'value 2', +}; + +console.log(obj[key1]); // value 1 +console.log(obj[key2]); // value 2 +``` + +У WeakMap і WeakSet сімвалы цяпер дапушчальныя ў якасці ключоў. + +## Дырэктывы Triple-Slash (Triple-Slash Directives) + +Дырэктывы Triple-slash (патройная касая рыса) — гэта спецыяльныя каментарыі, якія даюць інструкцыі кампілятару аб тым, як апрацоўваць файл. Гэтыя дырэктывы пачынаюцца з трох паслядоўных касых рыс (`///`) і звычайна размяшчаюцца ў пачатку файла TypeScript і не ўплываюць на паводзіны падчас выканання. + +Дырэктывы Triple-slash выкарыстоўваюцца для спасылкі на знешнія залежнасці, указання паводзін загрузкі модуляў, уключэння/выключэння пэўных функцый кампілятара і многага іншага. Некалькі прыкладаў: + +Спасылка на файл дэкларацыі: + + +```typescript +/// +``` + +Указанне фармату модуля: + + +```typescript +/// +``` + +Уключэнне опцый кампілятара, у наступным прыкладзе строгі рэжым: + + +```typescript +/// +``` + +## Маніпуляцыя тыпамі + +### Стварэнне тыпаў з тыпаў + +Магчыма ствараць новыя тыпы, кампануючы, маніпулюючы або трансфармуючы існуючыя тыпы. + +Тыпы перасячэння (`&`): + +Дазваляюць аб'ядноўваць некалькі тыпаў у адзін тып: + +```typescript +type A = { foo: number }; +type B = { bar: string }; +type C = A & B; // Intersection of A and B +const obj: C = { foo: 42, bar: 'hello' }; +``` + +Тыпы аб'яднання (`|`): + +Дазваляюць вызначыць тып, які можа быць адным з некалькіх тыпаў: + +```typescript +type Result = string | number; +const value1: Result = 'hello'; +const value2: Result = 42; +``` + +Супастаўленыя тыпы (Mapped Types): + +Дазваляюць трансфармаваць уласцівасці існуючага тыпу для стварэння новага тыпу: + +```typescript +type Mutable = { + readonly [P in keyof T]: T[P]; +}; +type Person = { + name: string; + age: number; +}; +type ImmutablePerson = Mutable; // Properties become read-only +``` + +Умоўныя тыпы (Conditional types): + +Дазваляюць ствараць тыпы на аснове пэўных умоў: + +```typescript +type ExtractParam = T extends (param: infer P) => any ? P : never; +type MyFunction = (name: string) => number; +type ParamType = ExtractParam; // string +``` + +### Тыпы індэксаванага доступу + +У TypeScript можна атрымліваць доступ і маніпуляваць тыпамі ўласцівасцей унутры іншага тыпу, выкарыстоўваючы індэкс, `Type[Key]`. + +```typescript +type Person = { + name: string; + age: number; +}; + +type AgeType = Person['age']; // number +``` + +```typescript +type MyTuple = [string, number, boolean]; +type MyType = MyTuple[2]; // boolean +``` + +### Утылітныя тыпы (Utility Types) + +Некалькі ўбудаваных утылітных тыпаў могуць быць выкарыстаны для маніпуляцыі тыпамі, ніжэй прыведзены спіс найбольш часта выкарыстоўваных: + +#### Awaited\ + +Канструюе тып, які рэкурсіўна разгортвае (unwraps) тыпы Promise. + +```typescript +type A = Awaited>; // string +``` + +#### Partial\ + +Канструюе тып, у якім усе ўласцівасці T устаноўлены як неабавязковыя (optional). + +```typescript +type Person = { + name: string; + age: number; +}; + +type A = Partial; // { name?: string | undefined; age?: number | undefined; } +``` + +#### Required\ + +Канструюе тып, у якім усе ўласцівасці T устаноўлены як абавязковыя (required). + +```typescript +type Person = { + name?: string; + age?: number; +}; + +type A = Required; // { name: string; age: number; } +``` + +#### Readonly\ + +Канструюе тып, у якім усе ўласцівасці T устаноўлены як толькі для чытання (readonly). + + +```typescript +type Person = { + name: string; + age: number; +}; + +type A = Readonly; + +const a: A = { name: 'Simon', age: 17 }; +a.name = 'John'; // Invalid +``` + +#### Record\ + +Канструюе тып з наборам уласцівасцей K тыпу T. + +```typescript +type Product = { + name: string; + price: number; +}; + +const products: Record = { + apple: { name: 'Apple', price: 0.5 }, + banana: { name: 'Banana', price: 0.25 }, +}; + +console.log(products.apple); // { name: 'Apple', price: 0.5 } +``` + +#### Pick\ + +Канструюе тып, выбіраючы паказаныя ўласцівасці K з T. + +```typescript +type Product = { + name: string; + price: number; +}; + +type Price = Pick; // { price: number; } +``` + +#### Omit\ + +Канструюе тып, выключаючы паказаныя ўласцівасці K з T. + +```typescript +type Product = { + name: string; + price: number; +}; + +type Name = Omit; // { name: string; } +``` + +#### Exclude\ + +Канструюе тып, выключаючы ўсе значэнні тыпу U з T. + +```typescript +type Union = 'a' | 'b' | 'c'; +type MyType = Exclude; // b +``` + +#### Extract\ + +Канструюе тып, здабываючы ўсе значэнні тыпу U з T. + +```typescript +type Union = 'a' | 'b' | 'c'; +type MyType = Extract; // a | c +``` + +#### NonNullable\ + +Канструюе тып, выключаючы null і undefined з T. + +```typescript +type Union = 'a' | null | undefined | 'b'; +type MyType = NonNullable; // 'a' | 'b' +``` + +#### Parameters\ + +Выцягвае тыпы параметраў функцыянальнага тыпу T. + +```typescript +type Func = (a: string, b: number) => void; +type MyType = Parameters; // [a: string, b: number] +``` + +#### ConstructorParameters\ + +Выцягвае тыпы параметраў тыпу функцыі канструктара T. + +```typescript +class Person { + constructor( + public name: string, + public age: number + ) {} +} +type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] +const params: PersonConstructorParams = ['John', 30]; +const person = new Person(...params); +console.log(person); // Person { name: 'John', age: 30 } +``` + +#### ReturnType\ + +Выцягвае тып вяртання функцыянальнага тыпу T. + +```typescript +type Func = (name: string) => number; +type MyType = ReturnType; // number +``` + +#### InstanceType\ + +Выцягвае тып асобніка класавага тыпу T. + +```typescript +class Person { + name: string; + + constructor(name: string) { + this.name = name; + } + + sayHello() { + console.log(`Hello, my name is ${this.name}!`); + } +} + +type PersonInstance = InstanceType; + +const person: PersonInstance = new Person('John'); + +person.sayHello(); // Hello, my name is John! +``` + +#### ThisParameterType\ + +Выцягвае тып параметра 'this' з функцыянальнага тыпу T. + +```typescript +interface Person { + name: string; + greet(this: Person): void; +} +type PersonThisType = ThisParameterType; // Person +``` + +#### OmitThisParameter\ + +Выдаляе параметр 'this' з функцыянальнага тыпу T. + +```typescript +function capitalize(this: String) { + return this[0].toUpperCase + this.substring(1).toLowerCase(); +} + +type CapitalizeType = OmitThisParameter; // () => string +``` + +#### ThisType\ + +Служыць маркёрам для кантэкстнага тыпу 'this'. + + +```typescript +type Logger = { + log: (error: string) => void; +}; + +let helperFunctions: { [name: string]: Function } & ThisType = { + hello: function () { + this.log('some error'); // Valid as "log" is a part of "this". + this.update(); // Invalid + }, +}; +``` + +#### Uppercase\ + +Пераўтварае імя ўваходнага тыпу T у верхні рэгістр. + +```typescript +type MyType = Uppercase<'abc'>; // "ABC" +``` + +#### Lowercase\ + +Пераўтварае імя ўваходнага тыпу T у ніжні рэгістр. + +```typescript +type MyType = Lowercase<'ABC'>; // "abc" +``` + +#### Capitalize\ + +Піша з вялікай літары імя ўваходнага тыпу T. + +```typescript +type MyType = Capitalize<'abc'>; // "Abc" +``` + +#### Uncapitalize\ + +Піша з малой літары імя ўваходнага тыпу T. + +```typescript +type MyType = Uncapitalize<'Abc'>; // "abc" +``` + +#### NoInfer\ + +NoInfer — гэта ўтылітны тып, прызначаны для блакіроўкі аўтаматычнага вываду тыпаў у межах вобласці бачнасці абагульненай функцыі. + +Прыклад: + +```typescript +// Automatic inference of types within the scope of a generic function. +function fn(x: T[], y: T) { + return x.concat(y); +} +const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[] +``` + +З NoInfer: + + +```typescript +// Example function that uses NoInfer to prevent type inference +function fn2(x: T[], y: NoInfer) { + return x.concat(y); +} + +const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'. +``` + +## Іншае + +### Памылкі і апрацоўка выключэнняў + +TypeScript дазваляе перахопліваць і апрацоўваць памылкі з дапамогай стандартных механізмаў апрацоўкі памылак JavaScript: + +Блокі Try-Catch-Finally: + +```typescript +try { + // Code that might throw an error +} catch (error) { + // Handle the error +} finally { + // Code that always executes, finally is optional +} +``` + +Вы таксама можаце апрацоўваць розныя тыпы памылак: + +```typescript +try { + // Code that might throw different types of errors +} catch (error) { + if (error instanceof TypeError) { + // Handle TypeError + } else if (error instanceof RangeError) { + // Handle RangeError + } else { + // Handle other errors + } +} +``` + +Карыстальніцкія тыпы памылак: + +Можна вызначыць больш канкрэтныя памылкі шляхам пашырэння класа `Error`: + +```typescript +class CustomError extends Error { + constructor(message: string) { + super(message); + this.name = 'CustomError'; + } +} + +throw new CustomError('This is a custom error.'); +``` + +### Класы Mixin + +Класы Mixin дазваляюць аб'ядноўваць і кампанаваць паводзіны некалькіх класаў у адзін клас. Яны забяспечваюць спосаб паўторнага выкарыстання і пашырэння функцыянальнасці без неабходнасці глыбокіх ланцужкоў успадкоўвання. + +```typescript +abstract class Identifiable { + name: string = ''; + logId() { + console.log('id:', this.name); + } +} +abstract class Selectable { + selected: boolean = false; + select() { + this.selected = true; + console.log('Select'); + } + deselect() { + this.selected = false; + console.log('Deselect'); + } +} +class MyClass { + constructor() {} +} + +// Extend MyClass to include the behavior of Identifiable and Selectable +interface MyClass extends Identifiable, Selectable {} + +// Function to apply mixins to a class +function applyMixins(source: any, baseCtors: any[]) { + baseCtors.forEach(baseCtor => { + Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { + let descriptor = Object.getOwnPropertyDescriptor( + baseCtor.prototype, + name + ); + if (descriptor) { + Object.defineProperty(source.prototype, name, descriptor); + } + }); + }); +} + +// Apply the mixins to MyClass +applyMixins(MyClass, [Identifiable, Selectable]); +let o = new MyClass(); +o.name = 'abc'; +o.logId(); +o.select(); +``` + +### Асінхронныя магчымасці мовы + +Паколькі TypeScript з'яўляецца надмноствам JavaScript, ён мае ўбудаваныя асінхронныя магчымасці мовы JavaScript, такія як: + +Прамісы (Promises): + +Прамісы — гэта спосаб апрацоўкі асінхронных аперацый і іх вынікаў з дапамогай такіх метадаў, як `.then()` і `.catch()`, для апрацоўкі ўмоў поспеху і памылак. + +Каб даведацца больш: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) + +Async/await: + +Ключавыя словы Async/await — гэта спосаб забяспечыць больш сінхронны сінтаксіс для працы з прамісамі. Ключавое слова `async` выкарыстоўваецца для вызначэння асінхроннай функцыі, а ключавое слова `await` выкарыстоўваецца ўнутры асінхроннай функцыі для прыпынення выканання да таго часу, пакуль праміс не будзе вырашаны або адхілены. + +Каб даведацца больш: +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) + +Наступныя API добра падтрымліваюцца ў TypeScript: + +Fetch API: +[https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) + +Web Workers: +[https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) + +Shared Workers: +[https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) + +WebSocket: +[https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) + +### Ітэратары і генератары + +І ітэратары, і генератары добра падтрымліваюцца ў TypeScript. + +Ітэратары — гэта аб'екты, якія рэалізуюць пратакол ітэратара, забяспечваючы спосаб доступу да элементаў калекцыі або паслядоўнасці па адным. Гэта структура, якая змяшчае паказальнік на наступны элемент у ітэрацыі. Яны маюць метад `next()`, які вяртае наступнае значэнне ў паслядоўнасці разам з лагічным значэннем, якое паказвае, ці паслядоўнасць завершана (`done`). + +```typescript +class NumberIterator implements Iterable { + private current: number; + + constructor( + private start: number, + private end: number + ) { + this.current = start; + } + + public next(): IteratorResult { + if (this.current <= this.end) { + const value = this.current; + this.current++; + return { value, done: false }; + } else { + return { value: undefined, done: true }; + } + } + + [Symbol.iterator](): Iterator { + return this; + } +} + +const iterator = new NumberIterator(1, 3); + +for (const num of iterator) { + console.log(num); +} +``` + +Генератары — гэта спецыяльныя функцыі, вызначаныя з дапамогай сінтаксісу `function*`, якія спрашчаюць стварэнне ітэратараў. Яны выкарыстоўваюць ключавое слова `yield` для вызначэння паслядоўнасці значэнняў і аўтаматычнага прыпынення і аднаўлення выканання пры запыце значэнняў. + +Генератары палягчаюць стварэнне ітэратараў і асабліва карысныя для працы з вялікімі або бясконцымі паслядоўнасцямі. + +Прыклад: + +```typescript +function* numberGenerator(start: number, end: number): Generator { + for (let i = start; i <= end; i++) { + yield i; + } +} + +const generator = numberGenerator(1, 5); + +for (const num of generator) { + console.log(num); +} +``` + +TypeScript таксама падтрымлівае асінхронныя ітэратары і асінхронныя генератары. + +Каб даведацца больш: + +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) + +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) + +### Даведнік па TsDocs JSDoc + +Пры працы з кодавай базай JavaScript можна дапамагчы TypeScript вывесці правільны тып, выкарыстоўваючы каментарыі JSDoc з дадатковай анатацыяй для прадастаўлення інфармацыі аб тыпе. + +Прыклад: + +```typescript +/** + * Computes the power of a given number + * @constructor + * @param {number} base – The base value of the expression + * @param {number} exponent – The exponent value of the expression + */ +function power(base: number, exponent: number) { + return Math.pow(base, exponent); +} +power(10, 2); // function power(base: number, exponent: number): number +``` + +Поўная дакументацыя прадстаўлена па гэтай спасылцы: +[https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) + +З версіі 3.7 можна генераваць азначэнні тыпаў .d.ts з сінтаксісу JavaScript JSDoc. +Больш інфармацыі можна знайсці тут: +[https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) + +### @types + +Пакеты ў арганізацыі @types — гэта спецыяльныя пагадненні аб найменні пакетаў, якія выкарыстоўваюцца для прадастаўлення азначэнняў тыпаў для існуючых бібліятэк або модуляў JavaScript. Напрыклад, выкарыстанне: + +```shell +npm install --save-dev @types/lodash +``` + +Усталюе азначэнні тыпаў `lodash` у вашым бягучым праекце. + +Каб унесці свой уклад у азначэнні тыпаў пакета @types, калі ласка, адпраўце pull request у [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). + +### JSX + +JSX (JavaScript XML) — гэта пашырэнне сінтаксісу мовы JavaScript, якое дазваляе пісаць HTML-падобны код у файлах JavaScript або TypeScript. Звычайна выкарыстоўваецца ў React для вызначэння структуры HTML. + +TypeScript пашырае магчымасці JSX, забяспечваючы праверку тыпаў і статычны аналіз. + +Каб выкарыстоўваць JSX, вам трэба ўсталяваць опцыю кампілятара `jsx` у файле `tsconfig.json`. Два агульныя варыянты канфігурацыі: + +* "preserve": генеруе файлы .jsx з нязменным JSX. Гэтая опцыя паказвае TypeScript пакінуць сінтаксіс JSX як ёсць і не трансфармаваць яго ў працэсе кампіляцыі. Вы можаце выкарыстоўваць гэтую опцыю, калі ў вас ёсць асобны інструмент, напрыклад Babel, які апрацоўвае трансфармацыю. +* "react": уключае ўбудаваную трансфармацыю JSX у TypeScript. Будзе выкарыстоўвацца React.createElement. + +Усе варыянты даступныя тут: +[https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) + +### Модулі ES6 + +TypeScript падтрымлівае ES6 (ECMAScript 2015) і многія наступныя версіі. Гэта азначае, што вы можаце выкарыстоўваць сінтаксіс ES6, такі як стрэлачныя функцыі, шаблонныя літэралы, класы, модулі, дэструктурызацыю і многае іншае. + +Каб уключыць функцыі ES6 у сваім праекце, вы можаце паказаць уласцівасць `target` у tsconfig.json. + +Прыклад канфігурацыі: + +```json +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist" + }, + "include": ["src"] +} +``` + +### Аператар узвядзення ў ступень ES7 + +Аператар узвядзення ў ступень (`**`) вылічвае значэнне, атрыманае ўзвядзеннем першага аперанда ў ступень другога аперанда. Ён працуе падобна да `Math.pow()`, але з дадатковай магчымасцю прымаць BigInts у якасці аперандаў. +TypeScript цалкам падтрымлівае гэты аператар пры выкарыстанні `es2016` або больш позняй версіі ў якасці `target` у вашым файле tsconfig.json. + +```typescript +console.log(2 ** (2 ** 2)); // 16 +``` + +### Аператар for-await-of + +Гэта функцыя JavaScript, якая цалкам падтрымліваецца ў TypeScript і дазваляе вам перабіраць асінхронныя ітэраваныя аб'екты пачынаючы з мэтавай версіі es2018. + +```typescript +async function* asyncNumbers(): AsyncIterableIterator { + yield Promise.resolve(1); + yield Promise.resolve(2); + yield Promise.resolve(3); +} + +(async () => { + for await (const num of asyncNumbers()) { + console.log(num); + } +})(); +``` + +### Метаўласцівасць new.target + +Вы можаце выкарыстоўваць у TypeScript метаўласцівасць `new.target`, якая дазваляе вызначыць, ці былі функцыя або канструктар выкліканы з дапамогай аператара `new`. Гэта дазваляе вызначыць, ці быў аб'ект створаны ў выніку выкліку канструктара. + +```typescript +class Parent { + constructor() { + console.log(new.target); // Logs the constructor function used to create an instance + } +} + +class Child extends Parent { + constructor() { + super(); + } +} + +const parentX = new Parent(); // [Function: Parent] +const child = new Child(); // [Function: Child] +``` + +### Выразы дынамічнага імпарту + +Магчыма ўмоўна загружаць модулі або ляніва загружаць іх па патрабаванні, выкарыстоўваючы прапанову ECMAScript для дынамічнага імпарту, якая падтрымліваецца ў TypeScript. + +Сінтаксіс для выразаў дынамічнага імпарту ў TypeScript выглядае наступным чынам: + + +```typescript +async function renderWidget() { + const container = document.getElementById('widget'); + if (container !== null) { + const widget = await import('./widget'); // Dynamic import + widget.render(container); + } +} + +renderWidget(); +``` + +### "tsc –watch" + +Гэтая каманда запускае кампілятар TypeScript з параметрам `--watch` з магчымасцю аўтаматычнай перакампіляцыі файлаў TypeScript пры кожнай іх змене. + +```shell +tsc --watch +``` + +Пачынаючы з версіі TypeScript 4.9, маніторынг файлаў у асноўным абапіраецца на падзеі файлавай сістэмы, аўтаматычна звяртаючыся да апытання (polling), калі назіральнік на аснове падзей не можа быць усталяваны. + +### Аператар сцвярджэння non-null + +Аператар сцвярджэння non-null (постфіксны !), таксама званы Definite Assignment Assertions (сцвярджэнні вызначанага прысваення), — гэта функцыя TypeScript, якая дазваляе вам сцвярджаць, што зменная або ўласцівасць не роўныя `null` або `undefined`, нават калі статычны аналіз тыпаў TypeScript мяркуе, што гэта можа быць так. З дапамогай гэтай функцыі можна выдаліць любую відавочную праверку. + +```typescript +type Person = { + name: string; +}; + +const printName = (person?: Person) => { + console.log(`Name is ${person!.name}`); +}; +``` + +### Дэкларацыі па змаўчанні + +Дэкларацыі па змаўчанні выкарыстоўваюцца, калі зменнай або параметру прысвойваецца значэнне па змаўчанні. Гэта азначае, што калі для гэтай зменнай або параметра не ўказана значэнне, замест яго будзе выкарыстоўвацца значэнне па змаўчанні. + +```typescript +function greet(name: string = 'Anonymous'): void { + console.log(`Hello, ${name}!`); +} +greet(); // Hello, Anonymous! +greet('John'); // Hello, John! +``` + +### Апцыянальны ланцужок (Optional Chaining) + +Аператар апцыянальнага ланцужка `?.` працуе як звычайны аператар кропкі (`.`) для доступу да ўласцівасцей або метадаў. Аднак ён карэктна апрацоўвае значэнні `null` або `undefined`, завяршаючы выраз і вяртаючы `undefined` замест выдачы памылкі. + +```typescript +type Person = { + name: string; + age?: number; + address?: { + street?: string; + city?: string; + }; +}; + +const person: Person = { + name: 'John', +}; + +console.log(person.address?.city); // undefined +``` + +### Аператар нулявога аб'яднання (Nullish coalescing) + +Аператар нулявога аб'яднання `??` вяртае значэнне правага боку, калі левы бок роўны `null` або `undefined`; у адваротным выпадку ён вяртае значэнне левага боку. + +```typescript +const foo = null ?? 'foo'; +console.log(foo); // foo + +const baz = 1 ?? 'baz'; +const baz2 = 0 ?? 'baz'; +console.log(baz); // 1 +console.log(baz2); // 0 +``` + +### Тыпы шаблонных літэралаў + +Тыпы шаблонных літэралаў дазваляюць маніпуляваць значэннем радка на ўзроўні тыпу і генераваць новыя тыпы радкоў на аснове існуючых. Яны карысныя для стварэння больш выразных і дакладных тыпаў з аперацый на аснове радкоў. + +```typescript +type Department = 'engineering' | 'hr'; +type Language = 'english' | 'spanish'; +type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" +``` + +### Перагрузка функцый + +Перагрузка функцый дазваляе вызначаць некалькі сігнатур функцый для аднаго і таго ж імя функцыі, кожная з рознымі тыпамі параметраў і тыпам вяртання. +Калі вы выклікаеце перагружаную функцыю, TypeScript выкарыстоўвае прадстаўленыя аргументы для вызначэння правільнай сігнатуры функцыі: + +```typescript +function makeGreeting(name: string): string; +function makeGreeting(names: string[]): string[]; + +function makeGreeting(person: unknown): unknown { + if (typeof person === 'string') { + return `Hi ${person}!`; + } else if (Array.isArray(person)) { + return person.map(name => `Hi, ${name}!`); + } + throw new Error('Unable to greet'); +} + +makeGreeting('Simon'); +makeGreeting(['Simone', 'John']); +``` + +### Рэкурсіўныя тыпы + +Рэкурсіўны тып — гэта тып, які можа спасылацца на самога сябе. Гэта карысна для вызначэння структур даных, якія маюць іерархічную або рэкурсіўную структуру (патэнцыйна бясконцую ўкладзенасць), такіх як звязаныя спісы, дрэвы і графы. + +```typescript +type ListNode = { + data: T; + next: ListNode | undefined; +}; +``` + +### Рэкурсіўныя ўмоўныя тыпы + +У TypeScript можна вызначаць складаныя адносіны паміж тыпамі, выкарыстоўваючы логіку і рэкурсію. +Давайце разбярэм гэта простымі словамі: + +Умоўныя тыпы: дазваляюць вызначаць тыпы на аснове лагічных умоў: + +```typescript +type CheckNumber = T extends number ? 'Number' : 'Not a number'; +type A = CheckNumber<123>; // 'Number' +type B = CheckNumber<'abc'>; // 'Not a number' +``` + +Рэкурсія: азначае азначэнне тыпу, якое спасылаецца на самога сябе ў сваім уласным азначэнні: + +```typescript +type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; + +const data: Json = { + prop1: true, + prop2: 'prop2', + prop3: { + prop4: [], + }, +}; +``` + +Рэкурсіўныя ўмоўныя тыпы аб'ядноўваюць як умоўную логіку, так і рэкурсію. Гэта азначае, што азначэнне тыпу можа залежаць ад самога сябе праз умоўную логіку, ствараючы складаныя і гнуткія адносіны паміж тыпамі. + +```typescript +type Flatten = T extends Array ? Flatten : T; + +type NestedArray = [1, [2, [3, 4], 5], 6]; +type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 +``` + +### Падтрымка модуляў ECMAScript у Node + +Node.js дадаў падтрымку модуляў ECMAScript пачынаючы з версіі 15.3.0, а TypeScript мае падтрымку модуляў ECMAScript для Node.js пачынаючы з версіі 4.7. Гэтую падтрымку можна ўключыць, выкарыстоўваючы ўласцівасць `module` са значэннем `nodenext` у файле tsconfig.json. Вось прыклад: + +```json +{ + "compilerOptions": { + "module": "nodenext", + "outDir": "./lib", + "declaration": true + } +} +``` + +Node.js падтрымлівае два пашырэнні файлаў для модуляў: `.mjs` для модуляў ES і `.cjs` для модуляў CommonJS. Эквівалентнымі пашырэннямі файлаў у TypeScript з'яўляюцца `.mts` для модуляў ES і `.cts` для модуляў CommonJS. Калі кампілятар TypeScript транспалюе гэтыя файлы ў JavaScript, ён стварае файлы `.mjs` і `.cjs`. + +Калі вы хочаце выкарыстоўваць модулі ES у сваім праекце, вы можаце ўсталяваць уласцівасць `type` у значэнне "module" у вашым файле package.json. Гэта ўказвае Node.js разглядаць праект як праект модуля ES. + +Акрамя таго, TypeScript таксама падтрымлівае дэкларацыі тыпаў у файлах .d.ts. Гэтыя файлы дэкларацыі прадастаўляюць інфармацыю аб тыпе для бібліятэк або модуляў, напісаных на TypeScript, што дазваляе іншым распрацоўшчыкам выкарыстоўваць іх з функцыямі праверкі тыпаў і аўтазапаўнення TypeScript. + +### Функцыі сцвярджэння (Assertion Functions) + +У TypeScript функцыі сцвярджэння — гэта функцыі, якія паказваюць на праверку пэўнай умовы на аснове іх значэння вяртання. У сваёй найпрасцейшай форме функцыя сцвярджэння правярае прадстаўлены прэдыкат і выклікае памылку, калі прэдыкат ацэньваецца як false. + +```typescript +function isNumber(value: unknown): asserts value is number { + if (typeof value !== 'number') { + throw new Error('Not a number'); + } +} +``` + +Або можа быць аб'яўлена як функцыянальны выраз: + +```typescript +type AssertIsNumber = (value: unknown) => asserts value is number; +const isNumber: AssertIsNumber = value => { + if (typeof value !== 'number') { + throw new Error('Not a number'); + } +}; +``` + +Функцыі сцвярджэння маюць падабенства з вартаўнікамі тыпаў. Вартаўнікі тыпаў былі першапачаткова ўведзены для выканання праверак падчас выканання і забеспячэння тыпу значэння ў пэўнай вобласці бачнасці. +У прыватнасці, вартаўнік тыпу — гэта функцыя, якая ацэньвае прэдыкат тыпу і вяртае лагічнае значэнне, якое паказвае, ці з'яўляецца прэдыкат ісцінным або ілжывым. Гэта крыху адрозніваецца ад функцый сцвярджэння, дзе намер складаецца ў тым, каб выкінуць памылку, а не вярнуць false, калі прэдыкат не задаволены. + +Прыклад вартаўніка тыпу: + +```typescript +const isNumber = (value: unknown): value is number => typeof value === 'number'; +``` + +### Варыятыўныя тыпы картэжаў (Variadic Tuple Types) + +Варыятыўныя тыпы картэжаў — гэта функцыі, уведзеныя ў TypeScript версіі 4.0; давайце пачнем іх вывучэнне з паўтарэння таго, што такое картэж: + +Тып картэжа — гэта масіў, які мае пэўную даўжыню і ў якім вядомы тып кожнага элемента: + +```typescript +type Student = [string, number]; +const [name, age]: Student = ['Simone', 20]; +``` + +Тэрмін «варыятыўны» (variadic) азначае нявызначаную арнасць (accept a variable number of arguments — прымаць зменную колькасць аргументаў). + +Варыятыўны картэж — гэта тып картэжа, які мае ўсе ўласцівасці, што і раней, але дакладная форма якога яшчэ не вызначана: + +```typescript +type Bar = [boolean, ...T, number]; + +type A = Bar<[boolean]>; // [boolean, boolean, number] +type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] +type C = Bar<[]>; // [boolean, number] +``` + +У папярэднім кодзе мы бачым, што форма картэжа вызначаецца перададзеным абагульненым тыпам `T`. + +Варыятыўныя картэжы могуць прымаць некалькі абагульненых тыпаў, што робіць іх вельмі гнуткімі: + +```typescript +type Bar = [...T, boolean, ...G]; + +type A = Bar<[number], [string]>; // [number, boolean, string] +type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] +``` + +З новымі варыятыўнымі картэжамі мы можам выкарыстоўваць: + +* Распаўсюджванні (spreads) у сінтаксісе тыпу картэжа цяпер могуць быць абагульненымі, таму мы можам прадстаўляць аперацыі вышэйшага парадку над картэжамі і масівамі, нават калі мы не ведаем фактычных тыпаў, з якімі мы працуем. +* Элементы rest могуць сустракацца ў любым месцы картэжа. + +Прыклад: + +```typescript +type Items = readonly unknown[]; + +function concat( + arr1: T, + arr2: U +): [...T, ...U] { + return [...arr1, ...arr2]; +} + +concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] +``` + +### Упакаваныя тыпы (Boxed types) + +Упакаваныя тыпы (Boxed types) адносяцца да аб'ектаў-абгортак, якія выкарыстоўваюцца для прадстаўлення прымітыўных тыпаў у выглядзе аб'ектаў. Гэтыя аб'екты-абгорткі забяспечваюць дадатковую функцыянальнасць і метады, якія недаступныя непасрэдна ў прымітыўных значэннях. + +Калі вы звяртаецеся да такога метаду, як `charAt` або `normalize`, на прымітыве `string`, JavaScript загортвае яго ў аб'ект `String`, выклікае метад, а затым выкідвае аб'ект. + +Дэманстрацыя: + +```typescript +const originalNormalize = String.prototype.normalize; +String.prototype.normalize = function () { + console.log(this, typeof this); + return originalNormalize.call(this); +}; +console.log('\u0041'.normalize()); +``` + +TypeScript адлюстроўвае гэтае адрозненне, забяспечваючы асобныя тыпы для прымітываў і адпаведных ім аб'ектаў-абгортак: + +* string => String +* number => Number +* boolean => Boolean +* symbol => Symbol +* bigint => BigInt + +Упакаваныя тыпы звычайна не патрэбныя. Пазбягайце выкарыстання ўпакаваных тыпаў і замест гэтага выкарыстоўвайце тып для прымітываў, напрыклад `string` замест `String`. + +### Каварыянтнасць і контраварыянтнасць у TypeScript + +Каварыянтнасць і контраварыянтнасць выкарыстоўваюцца для апісання таго, як працуюць адносіны пры працы з успадкоўваннем або прысваеннем тыпаў. + +Каварыянтнасць азначае, што адносіны паміж тыпамі захоўваюць кірунак успадкоўвання або прысваення, таму, калі тып A з'яўляецца падтыпам тыпу B, то масіў тыпу A таксама лічыцца падтыпам масіва тыпу B. Важна адзначыць, што адносіны падтыпаў захоўваюцца; гэта азначае, што каварыянтнасць прымае падтып, але не прымае супертып. + +Контраварыянтнасць азначае, што адносіны паміж тыпамі змяняюць кірунак успадкоўвання або прысваення на адваротны, таму, калі тып A з'яўляецца падтыпам тыпу B, то масіў тыпу B лічыцца падтыпам масіва тыпу A. Адносіны падтыпаў змяняюцца на адваротныя; гэта азначае, што контраварыянтнасць прымае супертып, але не прымае падтып. + +Заўвагі: Біварыянтнасць азначае прыняцце як супертыпу, так і падтыпу. + +Прыклад: Давайце ўявім, што ў нас ёсць месца для ўсіх жывёл і асобнае месца толькі для сабак. + +Пры каварыянтнасці вы можаце змясціць усіх сабак у месца для жывёл, таму што сабакі — гэта разнавіднасць жывёл. Але вы не можаце змясціць усіх жывёл у месца для сабак, бо там могуць быць прымешаны іншыя жывёлы. + +Пры контраварыянтнасці вы не можаце змясціць усіх жывёл у месца для сабак, бо ў месцы для жывёл могуць знаходзіцца і іншыя жывёлы. Аднак вы можаце змясціць усіх сабак у месца для жывёл, таму што ўсе сабакі таксама з'яўляюцца жывёламі. + + +```typescript +// Covariance example +class Animal { + name: string; + constructor(name: string) { + this.name = name; + } +} + +class Dog extends Animal { + breed: string; + constructor(name: string, breed: string) { + super(name); + this.breed = breed; + } +} + +let animals: Animal[] = []; +let dogs: Dog[] = []; + +// Covariance allows assigning subtype (Dog) array to supertype (Animal) array +animals = dogs; +dogs = animals; // Invalid: Type 'Animal[]' is not assignable to type 'Dog[]' + +// Contravariance example +type Feed = (animal: T) => void; + +let feedAnimal: Feed = (animal: Animal) => { + console.log(`Animal name: ${animal.name}`); +}; + +let feedDog: Feed = (dog: Dog) => { + console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); +}; + +// Contravariance allows assigning supertype (Animal) callback to subtype (Dog) callback +feedDog = feedAnimal; +feedAnimal = feedDog; // Invalid: Type 'Feed' is not assignable to type 'Feed'. +``` + +У TypeScript адносіны тыпаў для масіваў з'яўляюцца каварыянтнымі, у той час як адносіны тыпаў для параметраў функцыі з'яўляюцца контраварыянтнымі. Гэта азначае, што TypeScript дэманструе як каварыянтнасць, так і контраварыянтнасць, у залежнасці ад кантэксту. + +#### Неабавязковыя анатацыі варыянтнасці для параметраў тыпу + +Пачынаючы з TypeScript 4.7.0, мы можам выкарыстоўваць ключавыя словы `out` і `in`, каб канкрэтызаваць анатацыю варыянтнасці. + +Для каварыянтнасці (Covariant) выкарыстоўвайце ключавое слова `out`: + +```typescript +type AnimalCallback = () => T; // T is Covariant here +``` + +А для контраварыянтнасці (Contravariant) выкарыстоўвайце ключавое слова `in`: + +```typescript +type AnimalCallback = (value: T) => void; // T is Contravariance here +``` + +### Шаблонныя радковыя індэксныя сігнатуры + +Шаблонныя радковыя індэксныя сігнатуры дазваляюць нам вызначаць гнуткія індэксныя сігнатуры з выкарыстаннем шаблонаў радкоў. Гэтая функцыя дазваляе нам ствараць аб'екты, якія могуць індэксавацца з дапамогай пэўных шаблонаў радковых ключоў, забяспечваючы большы кантроль і спецыфічнасць пры доступе і маніпуляванні ўласцівасцямі. + +TypeScript пачынаючы з версіі 4.4 дазваляе індэксныя сігнатуры для сімвалаў і шаблонаў радкоў. + +```typescript +const uniqueSymbol = Symbol('description'); + +type MyKeys = `key-${string}`; + +type MyObject = { + [uniqueSymbol]: string; + [key: MyKeys]: number; +}; + +const obj: MyObject = { + [uniqueSymbol]: 'Unique symbol key', + 'key-a': 123, + 'key-b': 456, +}; + +console.log(obj[uniqueSymbol]); // Unique symbol key +console.log(obj['key-a']); // 123 +console.log(obj['key-b']); // 456 +``` + +### Аператар satisfies + +Аператар `satisfies` дазваляе праверыць, ці задавальняе дадзены тып пэўнаму інтэрфейсу або ўмове. Іншымі словамі, гэта гарантуе, што тып мае ўсе неабходныя ўласцівасці і метады канкрэтнага інтэрфейсу. Гэта спосаб пераканацца, што зменная ўпісваецца ў азначэнне тыпу. +Вось прыклад: + + +```typescript +type Columns = 'name' | 'nickName' | 'attributes'; + +type User = Record; + +// Type Annotation using `User` +const user: User = { + name: 'Simone', + nickName: undefined, + attributes: ['dev', 'admin'], +}; + +// In the following lines, TypeScript won't be able to infer properly +user.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. +user.nickName; // string | string[] | undefined + +// Type assertion using `as` +const user2 = { + name: 'Simon', + nickName: undefined, + attributes: ['dev', 'admin'], +} as User; + +// Here too, TypeScript won't be able to infer properly +user2.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. +user2.nickName; // string | string[] | undefined + +// Using `satisfies` operators we can properly infer the types now +const user3 = { + name: 'Simon', + nickName: undefined, + attributes: ['dev', 'admin'], +} satisfies User; + +user3.attributes?.map(console.log); // TypeScript infers correctly: string[] +user3.nickName; // TypeScript infers correctly: undefined +``` + +### Імпарт і экспарт толькі тыпаў + +Імпарт і экспарт толькі тыпаў (Type-Only Imports and Export) дазваляе імпартаваць або экспартаваць тыпы без імпарту або экспарту значэнняў або функцый, звязаных з гэтымі тыпамі. Гэта можа быць карысна для памяншэння памеру вашага пакета (bundle). + +Каб выкарыстоўваць імпарт толькі тыпаў, вы можаце выкарыстоўваць ключавое слова `import type`. + +TypeScript дазваляе выкарыстоўваць пашырэнні файлаў як дэкларацый, так і рэалізацый (.ts, .mts, .cts і .tsx) пры імпарце толькі тыпаў, незалежна ад налад `allowImportingTsExtensions`. + +Напрыклад: + + +```typescript +import type { House } from './house.ts'; +``` + +Падтрымліваюцца наступныя формы: + + +```typescript +import type T from './mod'; +import type { A, B } from './mod'; +import type * as Types from './mod'; +export type { T }; +export type { T } from './mod'; +``` + +### Дэкларацыя using і відавочнае кіраванне рэсурсамі + +Дэкларацыя `using` — гэта нязменнае звязванне з вобласцю бачнасці блока, падобнае да `const`, якое выкарыстоўваецца для кіравання аднаразовымі рэсурсамі. Пры ініцыялізацыі значэннем метад `Symbol.dispose` гэтага значэння запісваецца і пасля выконваецца пры выхадзе з ахопліваючай вобласці бачнасці блока. + +Гэта заснавана на функцыі кіравання рэсурсамі ECMAScript, якая карысная для выканання істотных задач ачысткі пасля стварэння аб'екта, такіх як закрыццё злучэнняў, выдаленне файлаў і вызваленне памяці. + +Заўвагі: + +* З-за нядаўняга ўвядзення ў TypeScript версіі 5.2 большасць асяроддзяў выканання не маюць убудаванай падтрымкі. Вам спатрэбяцца поліфілы (polyfills) для: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. +* Акрамя таго, вам трэба будзе наладзіць ваш `tsconfig.json` наступным чынам: + +```json +{ + "compilerOptions": { + "target": "es2022", + "lib": ["es2022", "esnext.disposable", "dom"] + } +} +``` + +Прыклад: + + +```typescript +//@ts-ignore +Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polify + +const doWork = (): Disposable => { + return { + [Symbol.dispose]: () => { + console.log('disposed'); + }, + }; +}; + +console.log(1); + +{ + using work = doWork(); // Resource is declared + console.log(2); +} // Resource is disposed (e.g., `work[Symbol.dispose]()` is evaluated) + +console.log(3); +``` + +Код выведзе ў журнал: + +```shell +1 +2 +disposed +3 +``` + +Рэсурс, які падлягае вызваленню (disposal), павінен адпавядаць інтэрфейсу `Disposable`: + +```typescript +// lib.esnext.disposable.d.ts +interface Disposable { + [Symbol.dispose](): void; +} +``` + +Дэкларацыі `using` запісваюць аперацыі вызвалення рэсурсаў у стэк, гарантуючы, што яны вызваляюцца ў парадку, адваротным аб'яўленню: + + +```typescript +{ + using j = getA(), + y = getB(); + using k = getC(); +} // disposes `C`, then `B`, then `A`. +``` + +Рэсурсы гарантавана будуць вызвалены, нават калі адбудзецца выкананне наступнага кода або ўзнікнуць выключэнні. Гэта можа прывесці да таго, што вызваленне патэнцыйна выкліча выключэнне, магчыма, заглушаючы іншае. Каб захаваць інфармацыю аб заглушаных памылках, уводзіцца новае натыўнае выключэнне `SuppressedError`. + +#### Дэкларацыя await using + +Дэкларацыя `await using` апрацоўвае асінхронна вызваляемы рэсурс. Значэнне павінна мець метад `Symbol.asyncDispose`, які будзе чакацца (awaited) у канцы блока. + + +```typescript +async function doWorkAsync() { + await using work = doWorkAsync(); // Resource is declared +} // Resource is disposed (e.g., `await work[Symbol.asyncDispose]()` is evaluated) +``` + +Асінхронна вызваляемы рэсурс павінен прытрымлівацца інтэрфейсу `Disposable` або `AsyncDisposable`: + +```typescript +// lib.esnext.disposable.d.ts +interface AsyncDisposable { + [Symbol.asyncDispose](): Promise; +} +``` + + +```typescript +//@ts-ignore +Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify + +class DatabaseConnection implements AsyncDisposable { + // A method that is called when the object is disposed asynchronously + [Symbol.asyncDispose]() { + // Close the connection and return a promise + return this.close(); + } + + async close() { + console.log('Closing the connection...'); + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log('Connection closed.'); + } +} + +async function doWork() { + // Create a new connection and dispose it asynchronously when it goes out of scope + await using connection = new DatabaseConnection(); // Resource is declared + console.log('Doing some work...'); +} // Resource is disposed (e.g., `await connection[Symbol.asyncDispose]()` is evaluated) + +doWork(); +``` + +Код выводзіць у журнал: + +```shell +Doing some work... +Closing the connection... +Connection closed. +``` + +Дэкларацыі `using` і `await using` дазволеныя ў аператарах: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. + +### Атрыбуты імпарту (Import Attributes) + +Атрыбуты імпарту (Import Attributes) у TypeScript 5.3 (цэтлікі для імпарту) паведамляюць асяроддзю выканання, як апрацоўваць модулі (JSON і г.д.). Гэта паляпшае бяспеку, забяспечваючы выразны імпарт і ўзгадненне з Палітыкай бяспекі кантэнту (CSP) для больш бяспечнай загрузкі рэсурсаў. TypeScript гарантуе, што яны сапраўдныя, але дазваляе асяроддзю выканання апрацоўваць іх інтэрпрэтацыю для апрацоўкі канкрэтнага модуля. + +Прыклад: + + +```typescript +import config from './config.json' with { type: 'json' }; +``` + +з дынамічным імпартам: + + +```typescript +const config = import('./config.json', { with: { type: 'json' } }); +``` diff --git a/README.md b/README.md index aebb8c23..2c72e189 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ This book has been translated into several language versions, including: [Italian](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) +[Belarusian](https://github.com/gibbok/typescript-book/blob/main/README-be_BY.md) + ## Downloads and website You can also download the Epub version: diff --git a/downloads/typescript-book-be_BY.epub b/downloads/typescript-book-be_BY.epub new file mode 100644 index 00000000..e18c6fb5 Binary files /dev/null and b/downloads/typescript-book-be_BY.epub differ diff --git a/downloads/typescript-book-be_BY.pdf b/downloads/typescript-book-be_BY.pdf new file mode 100644 index 00000000..16486623 Binary files /dev/null and b/downloads/typescript-book-be_BY.pdf differ diff --git a/downloads/typescript-book-it_IT.epub b/downloads/typescript-book-it_IT.epub index 07e71bd7..8b762fff 100644 Binary files a/downloads/typescript-book-it_IT.epub and b/downloads/typescript-book-it_IT.epub differ diff --git a/downloads/typescript-book-it_IT.pdf b/downloads/typescript-book-it_IT.pdf index 78908d5e..d64aa727 100644 Binary files a/downloads/typescript-book-it_IT.pdf and b/downloads/typescript-book-it_IT.pdf differ diff --git a/downloads/typescript-book-zh_CN.epub b/downloads/typescript-book-zh_CN.epub index 5a1da1fa..acf487dc 100644 Binary files a/downloads/typescript-book-zh_CN.epub and b/downloads/typescript-book-zh_CN.epub differ diff --git a/downloads/typescript-book-zh_CN.pdf b/downloads/typescript-book-zh_CN.pdf index a223c6ca..4196ab0f 100644 Binary files a/downloads/typescript-book-zh_CN.pdf and b/downloads/typescript-book-zh_CN.pdf differ diff --git a/downloads/typescript-book.epub b/downloads/typescript-book.epub index 1152e0ee..67c4034b 100644 Binary files a/downloads/typescript-book.epub and b/downloads/typescript-book.epub differ diff --git a/downloads/typescript-book.pdf b/downloads/typescript-book.pdf index f1113d7e..fe9426f2 100644 Binary files a/downloads/typescript-book.pdf and b/downloads/typescript-book.pdf differ diff --git a/tools/i18n.ts b/tools/i18n.ts index f0240e60..13eb4f13 100644 --- a/tools/i18n.ts +++ b/tools/i18n.ts @@ -2,7 +2,8 @@ export enum Language { en_EN = 'en_EN', zh_CN = 'zh_CN', it_IT = 'it_IT', + be_BY = 'be_BY', } -export type Languages = [Language.en_EN, Language.zh_CN, Language.it_IT] +export type Languages = [Language.en_EN, Language.zh_CN, Language.it_IT, Language.be_BY] -export const languages: Languages = [Language.en_EN, Language.zh_CN, Language.it_IT] \ No newline at end of file +export const languages: Languages = [Language.en_EN, Language.zh_CN, Language.it_IT, Language.be_BY] \ No newline at end of file diff --git a/tools/make-books.sh b/tools/make-books.sh index 31ff45e2..37064614 100755 --- a/tools/make-books.sh +++ b/tools/make-books.sh @@ -7,15 +7,18 @@ DIR_DOWNLOADS="downloads" INPUT_EN="README" INPUT_CN="README-zh_CN" INPUT_IT="README-it_IT" +INPUT_BE="README-be_BY" OUTPUT_EN="typescript-book" OUTPUT_CN="typescript-book-zh_CN" OUTPUT_IT="typescript-book-it_IT" +OUTPUT_BE="typescript-book-be_BY" AUTHOR="Simone Poggiali" TITLE_EN="The Concise TypeScript Book" TITLE_CN="# 简洁的TypeScript之书" TITLE_IT="The Concise TypeScript Book" +TITLE_BE="Лаканічная кніга па TypeScript" cd ../ @@ -45,15 +48,18 @@ fi pandoc -o $DIR_DOWNLOADS/$OUTPUT_EN.epub --metadata title="$TITLE_EN" --metadata author="$AUTHOR" -s $INPUT_EN.md pandoc -o $DIR_DOWNLOADS/$OUTPUT_CN.epub --metadata title="$TITLE_CN" --metadata author="$AUTHOR" -s $INPUT_CN.md pandoc -o $DIR_DOWNLOADS/$OUTPUT_IT.epub --metadata title="$TITLE_IT" --metadata author="$AUTHOR" -s $INPUT_IT.md +pandoc -o $DIR_DOWNLOADS/$OUTPUT_BE.epub --metadata title="$TITLE_BE" --metadata author="$AUTHOR" -s $INPUT_BE.md # Validate eBooks epubcheck $DIR_DOWNLOADS/$OUTPUT_CN.epub epubcheck $DIR_DOWNLOADS/$OUTPUT_CN.epub epubcheck $DIR_DOWNLOADS/$OUTPUT_IT.epub +epubcheck $DIR_DOWNLOADS/$OUTPUT_BE.epub # Generate PDFs ebook-convert $DIR_DOWNLOADS/$OUTPUT_EN.epub $DIR_DOWNLOADS/$OUTPUT_EN.pdf --pdf-page-numbers ebook-convert $DIR_DOWNLOADS/$OUTPUT_CN.epub $DIR_DOWNLOADS/$OUTPUT_CN.pdf --pdf-page-numbers ebook-convert $DIR_DOWNLOADS/$OUTPUT_IT.epub $DIR_DOWNLOADS/$OUTPUT_IT.pdf --pdf-page-numbers +ebook-convert $DIR_DOWNLOADS/$OUTPUT_BE.epub $DIR_DOWNLOADS/$OUTPUT_BE.pdf --pdf-page-numbers -echo "Books were created. Please commit!" \ No newline at end of file +echo "Books were created. Please commit!" diff --git a/tools/make-website-content.py b/tools/make-website-content.py index 5f04121d..1f45ca78 100644 --- a/tools/make-website-content.py +++ b/tools/make-website-content.py @@ -1,5 +1,5 @@ """ -Generate multiple Markdown documents from a single Markdown file by splitting it based on headings. +Generate multiple Markdown documents from a single Markdown file by splitting it based on headings. This script is designed for creating pages on a website and provides results for multiple languages. Note: the number of headings per language must be the same. """ @@ -9,7 +9,6 @@ import shutil from typing import List - # INPUT_FILE_PATH = "./test-md/README.md" # OUTPUT_DIR_PATH = "./test-md/en" # INPUT_FILE_PATH_CN = "./test-md/README-zh_CN.md" @@ -24,6 +23,9 @@ INPUT_FILE_PATH_IT = "../README-it_IT.md" OUTPUT_DIR_PATH_IT = "../website/src/content/docs/it-it/book" +INPUT_FILE_PATH_BE = "../README-be_BY.md" +OUTPUT_DIR_PATH_BE = "../website/src/content/docs/be-by/book" + def manage_output_dir(path: str) -> None: if os.path.exists(path): @@ -146,3 +148,5 @@ def process(base_input_path, input_lang_path: str, base_output_path: str) -> Non process(INPUT_FILE_PATH, INPUT_FILE_PATH_CN, OUTPUT_DIR_PATH_CN) process(INPUT_FILE_PATH, INPUT_FILE_PATH_IT, OUTPUT_DIR_PATH_IT) + +process(INPUT_FILE_PATH, INPUT_FILE_PATH_BE, OUTPUT_DIR_PATH_BE) diff --git a/tools/package-lock.json b/tools/package-lock.json index 198f32ca..d4771794 100644 --- a/tools/package-lock.json +++ b/tools/package-lock.json @@ -139,7 +139,8 @@ "version": "20.3.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.1.tgz", "integrity": "sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==", - "dev": true + "dev": true, + "peer": true }, "node_modules/@types/prettier": { "version": "2.7.3", @@ -845,6 +846,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/website/astro.config.mjs b/website/astro.config.mjs index fbb064be..81d71f38 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -41,6 +41,10 @@ export default defineConfig({ label: 'Italiano', lang: 'it-IT', }, + 'be-by': { + label: 'Беларуская', + lang: 'be-BY', + }, }, sidebar: [ { diff --git a/website/src/content/docs/be-by/book/about-the-author.md b/website/src/content/docs/be-by/book/about-the-author.md new file mode 100644 index 00000000..05fcddc6 --- /dev/null +++ b/website/src/content/docs/be-by/book/about-the-author.md @@ -0,0 +1,17 @@ +--- +title: About the Author +sidebar: + order: 6 + label: 6. About the Author +--- + + +Simone Poggiali is an experienced Staff Engineer with a passion for writing professional-grade code since the 90s. Throughout his international career, he has contributed to numerous projects for a wide range of clients, from startups to large organizations. Notable companies such as HelloFresh, Siemens, O2, Leroy Merlin and Snowplow have benefited from his expertise and dedication. + +You can reach Simone Poggiali on the following platforms: + +* LinkedIn: [https://www.linkedin.com/in/simone-poggiali](https://www.linkedin.com/in/simone-poggiali) +* GitHub: [https://github.com/gibbok](https://github.com/gibbok) +* X.com: [https://x.com/gibbok_coding](https://x.com/gibbok_coding) +* Email: gibbok.coding📧gmail.com + diff --git a/website/src/content/docs/be-by/book/any-type.md b/website/src/content/docs/be-by/book/any-type.md new file mode 100644 index 00000000..20c2d5f2 --- /dev/null +++ b/website/src/content/docs/be-by/book/any-type.md @@ -0,0 +1,22 @@ +--- +title: Any type +sidebar: + order: 44 + label: 44. Any type +--- + + +The `any` type is a special type (universal supertype) that can be used to represent any type of value (primitives, objects, arrays, functions, errors, symbols). It is often used in situations where the type of a value is not known at compile time, or when working with values from external APIs or libraries that do not have TypeScript typings. + +By utilizing `any` type, you are indicating to the TypeScript compiler that values should be represented without any limitations. In order to maximizing type safety in your code consider the following: + +* Limit the usage of `any` to specific cases where the type is truly unknown. +* Do not return `any` types from a function as you will lose type safety in the code using that function weakening your type safety. +* Instead of `any` use `@ts-ignore` if you need to silence the compiler. + +```typescript +let value: any; +value = true; // Valid +value = 7; // Valid +``` + diff --git a/website/src/content/docs/be-by/book/assignments.md b/website/src/content/docs/be-by/book/assignments.md new file mode 100644 index 00000000..c0bdd1b8 --- /dev/null +++ b/website/src/content/docs/be-by/book/assignments.md @@ -0,0 +1,22 @@ +--- +title: Assignments +sidebar: + order: 21 + label: 21. Assignments +--- + + +TypeScript narrowing using assignments is a way to narrow the type of a variable based on the value assigned to it. When a variable is assigned a value, TypeScript infers its type based on the assigned value, and it narrows the type of the variable to match the inferred type. + +```typescript +let value: string | number; +value = 'hello'; +if (typeof value === 'string') { + console.log(value.toUpperCase()); +} +value = 42; +if (typeof value === 'number') { + console.log(value.toFixed(2)); +} +``` + diff --git a/website/src/content/docs/be-by/book/built-in-type-primitives.md b/website/src/content/docs/be-by/book/built-in-type-primitives.md new file mode 100644 index 00000000..3a61b0e8 --- /dev/null +++ b/website/src/content/docs/be-by/book/built-in-type-primitives.md @@ -0,0 +1,21 @@ +--- +title: Built-in Type Primitives +sidebar: + order: 49 + label: 49. Built-in Type Primitives +--- + + +TypeScript has several built-in type primitives that can be used to define variables, function parameters, and return types: + +* `number`: Represents numeric values, including integers and floating-point numbers. +* `string`: Represents textual data +* `boolean`: Represents logical values, which can be either true or false. +* `null`: Represents the absence of a value. +* `undefined`: Represents a value that has not been assigned or has not been defined. +* `symbol`: Represents a unique identifier. Symbols are typically used as keys for object properties. +* `bigint`: Represents arbitrary-precision integers. +* `any`: Represents a dynamic or unknown type. Variables of type any can hold values of any type, and they bypass type checking. +* `void`: Represents the absence of any type. It is commonly used as the return type of functions that do not return a value. +* `never`: Represents a type for values that never occur. It is typically used as the return type of functions that throw an error or enter an infinite loop. + diff --git a/website/src/content/docs/be-by/book/class.md b/website/src/content/docs/be-by/book/class.md new file mode 100644 index 00000000..3eefad88 --- /dev/null +++ b/website/src/content/docs/be-by/book/class.md @@ -0,0 +1,679 @@ +--- +title: Class +sidebar: + order: 54 + label: 54. Class +--- + + +### Class Common Syntax + +The `class` keyword is used in TypeScript to define a class. Below, you can see an example: + +```typescript +class Person { + private name: string; + private age: number; + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + public sayHi(): void { + console.log( + `Hello, my name is ${this.name} and I am ${this.age} years old.` + ); + } +} +``` + +The `class` keyword is used to define a class named "Person". + +The class has two private properties: name of type `string` and age of type `number`. + +The constructor is defined using the `constructor` keyword. It takes name and age as parameters and assigns them to the corresponding properties. + +The class has a `public` method named sayHi that logs a greeting message. + +To create an instance of a class in TypeScript, you can use the `new` keyword followed by the class name, followed by parentheses `()`. For instance: + + +```typescript +const myObject = new Person('John Doe', 25); +myObject.sayHi(); // Output: Hello, my name is John Doe and I am 25 years old. +``` + +### Constructor + +Constructors are special methods within a class that are used to initialize the object's properties when an instance of the class is created. + +```typescript +class Person { + public name: string; + public age: number; + + constructor(name: string, age: number) { + this.name = name; + this.age = age; + } + + sayHello() { + console.log( + `Hello, my name is ${this.name} and I'm ${this.age} years old.` + ); + } +} + +const john = new Person('Simon', 17); +john.sayHello(); +``` + +It is possible to overload a constructor using the following syntax: + +```typescript +type Sex = 'm' | 'f'; + +class Person { + name: string; + age: number; + sex: Sex; + + constructor(name: string, age: number, sex?: Sex); + constructor(name: string, age: number, sex: Sex) { + this.name = name; + this.age = age; + this.sex = sex ?? 'm'; + } +} + +const p1 = new Person('Simon', 17); +const p2 = new Person('Alice', 22, 'f'); +``` + +In TypeScript, it is possible to define multiple constructor overloads, but you can have only one implementation that must be compatible with all the overloads, this can be achieved by using an optional parameter. + +```typescript +class Person { + name: string; + age: number; + + constructor(); + constructor(name: string); + constructor(name: string, age: number); + constructor(name?: string, age?: number) { + this.name = name ?? 'Unknown'; + this.age = age ?? 0; + } + + displayInfo() { + console.log(`Name: ${this.name}, Age: ${this.age}`); + } +} + +const person1 = new Person(); +person1.displayInfo(); // Name: Unknown, Age: 0 + +const person2 = new Person('John'); +person2.displayInfo(); // Name: John, Age: 0 + +const person3 = new Person('Jane', 25); +person3.displayInfo(); // Name: Jane, Age: 25 +``` + +### Private and Protected Constructors + +In TypeScript, constructors can be marked as private or protected, which restricts their accessibility and usage. + +Private Constructors: +Can be called only within the class itself. Private constructors are often used in scenarios where you want to enforce a singleton pattern or restrict the creation of instances to a factory method within the class + +Protected Constructors: +Protected constructors are useful when you want to create a base class that should not be instantiated directly but can be extended by subclasses. + +```typescript +class BaseClass { + protected constructor() {} +} + +class DerivedClass extends BaseClass { + private value: number; + + constructor(value: number) { + super(); + this.value = value; + } +} + +// Attempting to instantiate the base class directly will result in an error +// const baseObj = new BaseClass(); // Error: Constructor of class 'BaseClass' is protected. + +// Create an instance of the derived class +const derivedObj = new DerivedClass(10); +``` + +### Access Modifiers + +Access Modifiers `private`, `protected`, and `public` are used to control the visibility and accessibility of class members, such as properties and methods, in TypeScript classes. These modifiers are essential for enforcing encapsulation and establishing boundaries for accessing and modifying the internal state of a class. + +The `private` modifier restricts access to the class member only within the containing class. + +The `protected` modifier allows access to the class member within the containing class and its derived classes. + +The `public` modifier provides unrestricted access to the class member, allowing it to be accessed from anywhere." + +### Get and Set + +Getters and setters are special methods that allow you to define custom access and modification behavior for class properties. They enable you to encapsulate the internal state of an object and provide additional logic when getting or setting the values of properties. +In TypeScript, getters and setters are defined using the `get` and `set` keywords respectively. Here's an example: + +```typescript +class MyClass { + private _myProperty: string; + + constructor(value: string) { + this._myProperty = value; + } + get myProperty(): string { + return this._myProperty; + } + set myProperty(value: string) { + this._myProperty = value; + } +} +``` + +### Auto-Accessors in Classes + +TypeScript version 4.9 adds support for auto-accessors, a forthcoming ECMAScript feature. They resemble class properties but are declared with the "accessor" keyword. + +```typescript +class Animal { + accessor name: string; + + constructor(name: string) { + this.name = name; + } +} +``` + +Auto-accessors are "de-sugared" into private `get` and `set` accessors, operating on an inaccessible property. + + +```typescript +class Animal { + #__name: string; + + get name() { + return this.#__name; + } + set name(value: string) { + this.#__name = name; + } + + constructor(name: string) { + this.name = name; + } +} +``` + +### this + +In TypeScript, the `this` keyword refers to the current instance of a class within its methods or constructors. It allows you to access and modify the properties and methods of the class from within its own scope. +It provides a way to access and manipulate the internal state of an object within its own methods. + +```typescript +class Person { + private name: string; + constructor(name: string) { + this.name = name; + } + public introduce(): void { + console.log(`Hello, my name is ${this.name}.`); + } +} + +const person1 = new Person('Alice'); +person1.introduce(); // Hello, my name is Alice. +``` + +### Parameter Properties + +Parameter properties allow you to declare and initialize class properties directly within the constructor parameters avoiding boilerplate code, example: + +```typescript +class Person { + constructor( + private name: string, + public age: number + ) { + // The "private" and "public" keywords in the constructor + // automatically declare and initialize the corresponding class properties. + } + public introduce(): void { + console.log( + `Hello, my name is ${this.name} and I am ${this.age} years old.` + ); + } +} +const person = new Person('Alice', 25); +person.introduce(); +``` + +### Abstract Classes + +Abstract Classes are used in TypeScript mainly for inheritance, they provide a way to define common properties and methods that can be inherited by subclasses. +This is useful when you want to define common behavior and enforce that subclasses implement certain methods. They provide a way to create a hierarchy of classes where the abstract base class provides a shared interface and common functionality for the subclasses. + +```typescript +abstract class Animal { + protected name: string; + + constructor(name: string) { + this.name = name; + } + + abstract makeSound(): void; +} + +class Cat extends Animal { + makeSound(): void { + console.log(`${this.name} meows.`); + } +} + +const cat = new Cat('Whiskers'); +cat.makeSound(); // Output: Whiskers meows. +``` + +### With Generics + +Classes with generics allow you to define reusable classes which can work with different types. + +```typescript +class Container { + private item: T; + + constructor(item: T) { + this.item = item; + } + + getItem(): T { + return this.item; + } + + setItem(item: T): void { + this.item = item; + } +} + +const container1 = new Container(42); +console.log(container1.getItem()); // 42 + +const container2 = new Container('Hello'); +container2.setItem('World'); +console.log(container2.getItem()); // World +``` + +### Decorators + +Decorators provide a mechanism to add metadata, modify behavior, validate, or extend the functionality of the target element. They are functions that execute at runtime. Multiple decorators can be applied to a declaration. + +Decorators are experimental features, and the following examples are only compatible with TypeScript version 5 or above using ES6. + +For TypeScript versions prior to 5, they should be enabled using the `experimentalDecorators` property in your `tsconfig.json` or by using `--experimentalDecorators` in your command line (but the following example won't work). + +Some of the common use cases for decorators include: + +* Watching property changes. +* Watching method calls. +* Adding extra properties or methods. +* Runtime validation. +* Automatic serialization and deserialization. +* Logging. +* Authorization and authentication. +* Error guarding. + +Note: Decorators for version 5 do not allow decorating parameters. + +Types of decorators: + +#### Class Decorators + +Class Decorators are useful for extending an existing class, such as adding properties or methods, or collecting instances of a class. In the following example, we add a `toString` method that converts the class into a string representation. + +```typescript +type Constructor = new (...args: any[]) => T; + +function toString( + Value: Class, + context: ClassDecoratorContext +) { + return class extends Value { + constructor(...args: any[]) { + super(...args); + console.log(JSON.stringify(this)); + console.log(JSON.stringify(context)); + } + }; +} + +@toString +class Person { + name: string; + + constructor(name: string) { + this.name = name; + } + + greet() { + return 'Hello, ' + this.name; + } +} +const person = new Person('Simon'); +/* Logs: +{"name":"Simon"} +{"kind":"class","name":"Person"} +*/ +``` + +#### Property Decorator + +Property decorators are useful for modifying the behavior of a property, such as changing the initialization values. In the following code, we have a script that sets a property to always be in uppercase: + +```typescript +function upperCase( + target: undefined, + context: ClassFieldDecoratorContext +) { + return function (this: T, value: string) { + return value.toUpperCase(); + }; +} + +class MyClass { + @upperCase + prop1 = 'hello!'; +} + +console.log(new MyClass().prop1); // Logs: HELLO! +``` + +#### Method Decorator + +Method decorators allow you to change or enhance the behavior of methods. Below is an example of a simple logger: + +```typescript +function log( + target: (this: This, ...args: Args) => Return, + context: ClassMethodDecoratorContext< + This, + (this: This, ...args: Args) => Return + > +) { + const methodName = String(context.name); + + function replacementMethod(this: This, ...args: Args): Return { + console.log(`LOG: Entering method '${methodName}'.`); + const result = target.call(this, ...args); + console.log(`LOG: Exiting method '${methodName}'.`); + return result; + } + + return replacementMethod; +} + +class MyClass { + @log + sayHello() { + console.log('Hello!'); + } +} + +new MyClass().sayHello(); +``` + +It logs: + +```shell +LOG: Entering method 'sayHello'. +Hello! +LOG: Exiting method 'sayHello'. +``` + +#### Getter and Setter Decorators + +Getter and setter decorators allow you to change or enhance the behavior of class accessors. They are useful, for instance, for validating property assignments. Here's a simple example for a getter decorator: + +```typescript +function range(min: number, max: number) { + return function ( + target: (this: This) => Return, + context: ClassGetterDecoratorContext + ) { + return function (this: This): Return { + const value = target.call(this); + if (value < min || value > max) { + throw 'Invalid'; + } + Object.defineProperty(this, context.name, { + value, + enumerable: true, + }); + return value; + }; + }; +} + +class MyClass { + private _value = 0; + + constructor(value: number) { + this._value = value; + } + @range(1, 100) + get getValue(): number { + return this._value; + } +} + +const obj = new MyClass(10); +console.log(obj.getValue); // Valid: 10 + +const obj2 = new MyClass(999); +console.log(obj2.getValue); // Throw: Invalid! +``` + +#### Decorator Metadata + +Decorator Metadata simplifies the process for decorators to apply and utilize metadata in any class. They can access a new metadata property on the context object, which can serve as a key for both primitives and objects. +Metadata information can be accessed on the class via `Symbol.metadata`. + +Metadata can be used for various purposes, such as debugging, serialization, or dependency injection with decorators. + +```typescript +//@ts-ignore +Symbol.metadata ??= Symbol('Symbol.metadata'); // Simple polify + +type Context = + | ClassFieldDecoratorContext + | ClassAccessorDecoratorContext + | ClassMethodDecoratorContext; // Context contains property metadata: DecoratorMetadata + +function setMetadata(_target: any, context: Context) { + // Set the metadata object with a primitive value + context.metadata[context.name] = true; +} + +class MyClass { + @setMetadata + a = 123; + + @setMetadata + accessor b = 'b'; + + @setMetadata + fn() {} +} + +const metadata = MyClass[Symbol.metadata]; // Get metadata information + +console.log(JSON.stringify(metadata)); // {"bar":true,"baz":true,"foo":true} +``` + +### Inheritance + +Inheritance refers to the mechanism by which a class can inherit properties and methods from another class, known as the base class or superclass. The derived class, also called the child class or subclass, can extend and specialize the functionality of the base class by adding new properties and methods or overriding existing ones. + +```typescript +class Animal { + name: string; + + constructor(name: string) { + this.name = name; + } + + speak(): void { + console.log('The animal makes a sound'); + } +} + +class Dog extends Animal { + breed: string; + + constructor(name: string, breed: string) { + super(name); + this.breed = breed; + } + + speak(): void { + console.log('Woof! Woof!'); + } +} + +// Create an instance of the base class +const animal = new Animal('Generic Animal'); +animal.speak(); // The animal makes a sound + +// Create an instance of the derived class +const dog = new Dog('Max', 'Labrador'); +dog.speak(); // Woof! Woof!" +``` + +TypeScript does not support multiple inheritance in the traditional sense and instead allows inheritance from a single base class. +TypeScript supports multiple interfaces. An interface can define a contract for the structure of an object, and a class can implement multiple interfaces. This allows a class to inherit behavior and structure from multiple sources. + +```typescript +interface Flyable { + fly(): void; +} + +interface Swimmable { + swim(): void; +} + +class FlyingFish implements Flyable, Swimmable { + fly() { + console.log('Flying...'); + } + + swim() { + console.log('Swimming...'); + } +} + +const flyingFish = new FlyingFish(); +flyingFish.fly(); +flyingFish.swim(); +``` + +The `class` keyword in TypeScript, similar to JavaScript, is often referred to as syntactic sugar. It was introduced in ECMAScript 2015 (ES6) to offer a more familiar syntax for creating and working with objects in a class-based manner. However, it's important to note that TypeScript, being a superset of JavaScript, ultimately compiles down to JavaScript, which remains prototype-based at its core. + +### Statics + +TypeScript has static members. To access the static members of a class, you can use the class name followed by a dot, without the need to create an object. + +```typescript +class OfficeWorker { + static memberCount: number = 0; + + constructor(private name: string) { + OfficeWorker.memberCount++; + } +} + +const w1 = new OfficeWorker('James'); +const w2 = new OfficeWorker('Simon'); +const total = OfficeWorker.memberCount; +console.log(total); // 2 +``` + +### Property initialization + +There are several ways how you can initialize properties for a class in TypeScript: + +Inline: + +In the following example these initial values will be used when an instance of the class is created. + +```typescript +class MyClass { + property1: string = 'default value'; + property2: number = 42; +} +``` + +In the constructor: + +```typescript +class MyClass { + property1: string; + property2: number; + + constructor() { + this.property1 = 'default value'; + this.property2 = 42; + } +} +``` + +Using constructor parameters: + +```typescript +class MyClass { + constructor( + private property1: string = 'default value', + public property2: number = 42 + ) { + // There is no need to assign the values to the properties explicitly. + } + log() { + console.log(this.property2); + } +} +const x = new MyClass(); +x.log(); +``` + +### Method overloading + +Method overloading allows a class to have multiple methods with the same name but different parameter types or a different number of parameters. This allows us to call a method in different ways based on the arguments passed. + +```typescript +class MyClass { + add(a: number, b: number): number; // Overload signature 1 + add(a: string, b: string): string; // Overload signature 2 + + add(a: number | string, b: number | string): number | string { + if (typeof a === 'number' && typeof b === 'number') { + return a + b; + } + if (typeof a === 'string' && typeof b === 'string') { + return a.concat(b); + } + throw new Error('Invalid arguments'); + } +} + +const r = new MyClass(); +console.log(r.add(10, 5)); // Logs 15 +``` + diff --git a/website/src/content/docs/be-by/book/common-built-in-js-objects.md b/website/src/content/docs/be-by/book/common-built-in-js-objects.md new file mode 100644 index 00000000..965ea440 --- /dev/null +++ b/website/src/content/docs/be-by/book/common-built-in-js-objects.md @@ -0,0 +1,29 @@ +--- +title: Common Built-in JS Objects +sidebar: + order: 50 + label: 50. Common Built-in JS Objects +--- + + +TypeScript is a superset of JavaScript, it includes all the commonly used built-in JavaScript objects. You can find an extensive list of these objects on the Mozilla Developer Network (MDN) documentation website: +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects) + +Here is a list of some commonly used built-in JavaScript objects: + +* Function +* Object +* Boolean +* Error +* Number +* BigInt +* Math +* Date +* String +* RegExp +* Array +* Map +* Set +* Promise +* Intl + diff --git a/website/src/content/docs/be-by/book/conditional-types.md b/website/src/content/docs/be-by/book/conditional-types.md new file mode 100644 index 00000000..88fe9ea1 --- /dev/null +++ b/website/src/content/docs/be-by/book/conditional-types.md @@ -0,0 +1,20 @@ +--- +title: Conditional Types +sidebar: + order: 39 + label: 39. Conditional Types +--- + + +Conditional Types are a way to create a type that depends on a condition, where the type to be created is determined based on the result of the condition. They are defined using the `extends` keyword and a ternary operator to conditionally choose between two types. + +```typescript +type IsArray = T extends any[] ? true : false; + +const myArray = [1, 2, 3]; +const myNumber = 42; + +type IsMyArrayAnArray = IsArray; // Type true +type IsMyNumberAnArray = IsArray; // Type false +``` + diff --git a/website/src/content/docs/be-by/book/control-flow-analysis.md b/website/src/content/docs/be-by/book/control-flow-analysis.md new file mode 100644 index 00000000..2f39a693 --- /dev/null +++ b/website/src/content/docs/be-by/book/control-flow-analysis.md @@ -0,0 +1,58 @@ +--- +title: Control Flow Analysis +sidebar: + order: 22 + label: 22. Control Flow Analysis +--- + + +Control Flow Analysis in TypeScript is a way to statically analyze the code flow to infer the types of variables, allowing the compiler to narrow the types of those variables as needed, based on the results of the analysis. + +Prior to TypeScript 4.4, code flow analysis would only be applied to code within an if statement, but from TypeScript 4.4, it can also be applied to conditional expressions and discriminant property accesses indirectly referenced through const variables. + +For example: + +```typescript +const f1 = (x: unknown) => { + const isString = typeof x === 'string'; + if (isString) { + x.length; + } +}; + +const f2 = ( + obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } +) => { + const isFoo = obj.kind === 'foo'; + if (isFoo) { + obj.foo; + } else { + obj.bar; + } +}; +``` + +Some examples where narrowing does not occur: + + +```typescript +const f1 = (x: unknown) => { + let isString = typeof x === 'string'; + if (isString) { + x.length; // Error, no narrowing because isString it is not const + } +}; + +const f6 = ( + obj: { kind: 'foo'; foo: string } | { kind: 'bar'; bar: number } +) => { + const isFoo = obj.kind === 'foo'; + obj = obj; + if (isFoo) { + obj.foo; // Error, no narrowing because obj is assigned in function body + } +}; +``` + +Notes: Up to five levels of indirection are analyzed in conditional expressions. + diff --git a/website/src/content/docs/be-by/book/differences-between-type-and-interface.md b/website/src/content/docs/be-by/book/differences-between-type-and-interface.md new file mode 100644 index 00000000..17096b2f --- /dev/null +++ b/website/src/content/docs/be-by/book/differences-between-type-and-interface.md @@ -0,0 +1,96 @@ +--- +title: Differences between Type and Interface +sidebar: + order: 53 + label: 53. Differences between Type and Interface +--- + + +Declaration merging (augmentation): + +Interfaces support declaration merging, which means that you can define multiple interfaces with the same name, and TypeScript will merge them into a single interface with the combined properties and methods. On the other hand, types do not support declaration merging. This can be helpful when you want to add extra functionality or customize existing types without modifying the original definitions or patching missing or incorrect types. + +```typescript +interface A { + x: string; +} +interface A { + y: string; +} +const j: A = { + x: 'xx', + y: 'yy', +}; +``` + +Extending other types/interfaces: + +Both types and interfaces can extend other types/interfaces, but the syntax is different. With interfaces, you use the `extends` keyword to inherit properties and methods from other interfaces. However, an interface cannot extend a complex type like a union type. + +```typescript +interface A { + x: string; + y: number; +} +interface B extends A { + z: string; +} +const car: B = { + x: 'x', + y: 123, + z: 'z', +}; +``` + +For types, you use the & operator to combine multiple types into a single type (intersection). + +```typescript +interface A { + x: string; + y: number; +} + +type B = A & { + j: string; +}; + +const c: B = { + x: 'x', + y: 123, + j: 'j', +}; +``` + +Union and Intersection Types: + +Types are more flexible when it comes to defining Union and Intersection Types. With the `type` keyword, you can easily create union types using the `|` operator and intersection types using the `&` operator. While interfaces can also represent union types indirectly, they don't have built-in support for intersection types. + +```typescript +type Department = 'dep-x' | 'dep-y'; // Union + +type Person = { + name: string; + age: number; +}; + +type Employee = { + id: number; + department: Department; +}; + +type EmployeeInfo = Person & Employee; // Intersection +``` + +Example with interfaces: + +```typescript +interface A { + x: 'x'; +} +interface B { + y: 'y'; +} + +type C = A | B; // Union of interfaces +``` + diff --git a/website/src/content/docs/be-by/book/discriminated-unions.md b/website/src/content/docs/be-by/book/discriminated-unions.md new file mode 100644 index 00000000..1fbcae10 --- /dev/null +++ b/website/src/content/docs/be-by/book/discriminated-unions.md @@ -0,0 +1,39 @@ +--- +title: Discriminated Unions +sidebar: + order: 24 + label: 24. Discriminated Unions +--- + + +Discriminated Unions in TypeScript are a type of union type that uses a common property, known as the discriminant, to narrow down the set of possible types for the union. + +```typescript +type Square = { + kind: 'square'; // Discriminant + size: number; +}; + +type Circle = { + kind: 'circle'; // Discriminant + radius: number; +}; + +type Shape = Square | Circle; + +const area = (shape: Shape) => { + switch (shape.kind) { + case 'square': + return Math.pow(shape.size, 2); + case 'circle': + return Math.PI * Math.pow(shape.radius, 2); + } +}; + +const square: Square = { kind: 'square', size: 5 }; +const circle: Circle = { kind: 'circle', radius: 2 }; + +console.log(area(square)); // 25 +console.log(area(circle)); // 12.566370614359172 +``` + diff --git a/website/src/content/docs/be-by/book/distributive-conditional-types.md b/website/src/content/docs/be-by/book/distributive-conditional-types.md new file mode 100644 index 00000000..e1a2c9a9 --- /dev/null +++ b/website/src/content/docs/be-by/book/distributive-conditional-types.md @@ -0,0 +1,17 @@ +--- +title: Distributive Conditional Types +sidebar: + order: 40 + label: 40. Distributive Conditional Types +--- + + +Distributive Conditional Types are a feature that allow a type to be distributed over a union of types, by applying a transformation to each member of the union individually. +This can be especially useful when working with mapped types or higher-order types. + +```typescript +type Nullable = T extends any ? T | null : never; +type NumberOrBool = number | boolean; +type NullableNumberOrBool = Nullable; // number | boolean | null +``` + diff --git a/website/src/content/docs/be-by/book/downloads-and-website.md b/website/src/content/docs/be-by/book/downloads-and-website.md new file mode 100644 index 00000000..f896fee0 --- /dev/null +++ b/website/src/content/docs/be-by/book/downloads-and-website.md @@ -0,0 +1,16 @@ +--- +title: Downloads and website +sidebar: + order: 3 + label: 3. Downloads and website +--- + + +You can also download the Epub version: + +[https://github.com/gibbok/typescript-book/tree/main/downloads](https://github.com/gibbok/typescript-book/tree/main/downloads) + +An online version is available at: + +[https://gibbok.github.io/typescript-book](https://gibbok.github.io/typescript-book) + diff --git a/website/src/content/docs/be-by/book/enums.md b/website/src/content/docs/be-by/book/enums.md new file mode 100644 index 00000000..ece7d377 --- /dev/null +++ b/website/src/content/docs/be-by/book/enums.md @@ -0,0 +1,167 @@ +--- +title: Enums +sidebar: + order: 19 + label: 19. Enums +--- + + +In TypeScript, an `enum` is a set of named constant values. + +```typescript +enum Color { + Red = '#ff0000', + Green = '#00ff00', + Blue = '#0000ff', +} +``` + +Enums can be defined in different ways: + +### Numeric enums + +In TypeScript, a Numeric Enum is an Enum where each constant is assigned a numeric value, starting from 0 by default. + +```typescript +enum Size { + Small, // value starts from 0 + Medium, + Large, +} +``` + +It is possible to specify custom values by explicitly assigning them: + +```typescript +enum Size { + Small = 10, + Medium, + Large, +} +console.log(Size.Medium); // 11 +``` + +### String enums + +In TypeScript, a String enum is an Enum where each constant is assigned a string value. + +```typescript +enum Language { + English = 'EN', + Spanish = 'ES', +} +``` + +Note: TypeScript allows the usage of heterogeneous Enums where string and numeric members can coexist. + +### Constant enums + +A constant enum in TypeScript is a special type of Enum where all the values are known at compile time and are inlined wherever the enum is used, resulting in more efficient code. + +```typescript +const enum Language { + English = 'EN', + Spanish = 'ES', +} +console.log(Language.English); +``` + +Will be compiled into: + +```typescript +console.log('EN' /* Language.English */); +``` + +Notes: +Const Enums have hardcoded values, erasing the Enum, which can be more efficient in self-contained libraries but is generally not desirable. Also, Const enums cannot have computed members. + +### Reverse mapping + +In TypeScript, reverse mappings in Enums refer to the ability to retrieve the Enum member name from its value. By default, Enum members have forward mappings from name to value, but reverse mappings can be created by explicitly setting values for each member. Reverse mappings are useful when you need to look up an Enum member by its value, or when you need to iterate over all the Enum members. Note that only numeric enums members will generate reverse mappings, while String Enum members do not get a reverse mapping generated at all. + +The following enum: + +```typescript +enum Grade { + A = 90, + B = 80, + C = 70, + F = 'fail', +} +``` + +Compiles to: + + +```javascript +'use strict'; +var Grade; +(function (Grade) { + Grade[(Grade['A'] = 90)] = 'A'; + Grade[(Grade['B'] = 80)] = 'B'; + Grade[(Grade['C'] = 70)] = 'C'; + Grade['F'] = 'fail'; +})(Grade || (Grade = {})); +``` + +Therefore, mapping values to keys works for numeric enum members, but not for string enum members: + + +```typescript +enum Grade { + A = 90, + B = 80, + C = 70, + F = 'fail', +} +const myGrade = Grade.A; +console.log(Grade[myGrade]); // A +console.log(Grade[90]); // A + +const failGrade = Grade.F; +console.log(failGrade); // fail +console.log(Grade[failGrade]); // Element implicitly has an 'any' type because index expression is not of type 'number'. +``` + +### Ambient enums + +An ambient enum in TypeScript is a type of Enum that is defined in a declaration file (*.d.ts) without an associated implementation. It allows you to define a set of named constants that can be used in a type-safe way across different files without having to import the implementation details in each file. + +### Computed and constant members + +In TypeScript, a computed member is a member of an Enum that has a value calculated at runtime, while a constant member is a member whose value is set at compile-time and cannot be changed during runtime. Computed members are allowed in regular Enums, while constant members are allowed in both regular and const enums. + +```typescript +// Constant members +enum Color { + Red = 1, + Green = 5, + Blue = Red + Green, +} +console.log(Color.Blue); // 6 generation at compilation time +``` + +```typescript +// Computed members +enum Color { + Red = 1, + Green = Math.pow(2, 2), + Blue = Math.floor(Math.random() * 3) + 1, +} +console.log(Color.Blue); // random number generated at run time +``` + +Enums are denoted by unions comprising their member types. The values of each member can be determined through constant or non-constant expressions, with members possessing constant values being assigned literal types. To illustrate, consider the declaration of type E and its subtypes E.A, E.B, and E.C. In this case, E represents the union E.A | E.B | E.C. + +```typescript +const identity = (value: number) => value; + +enum E { + A = 2 * 5, // Numeric literal + B = 'bar', // String literal + C = identity(42), // Opaque computed +} + +console.log(E.C); //42 +``` + diff --git a/website/src/content/docs/be-by/book/erased-structural-types.md b/website/src/content/docs/be-by/book/erased-structural-types.md new file mode 100644 index 00000000..ec555f51 --- /dev/null +++ b/website/src/content/docs/be-by/book/erased-structural-types.md @@ -0,0 +1,28 @@ +--- +title: Erased Structural Types +sidebar: + order: 56 + label: 56. Erased Structural Types +--- + + +In TypeScript, objects do not have to match a specific, exact type. For instance, if we create an object that fulfills an interface's requirements, we can utilize that object in places where that interface is required, even if there was no explicit connection between them. +Example: + +```typescript +type NameProp1 = { + prop1: string; +}; + +function log(x: NameProp1) { + console.log(x.prop1); +} + +const obj = { + prop2: 123, + prop1: 'Origin', +}; + +log(obj); // Valid +``` + diff --git a/website/src/content/docs/be-by/book/exhaustiveness-checking.md b/website/src/content/docs/be-by/book/exhaustiveness-checking.md new file mode 100644 index 00000000..1e0c2c2b --- /dev/null +++ b/website/src/content/docs/be-by/book/exhaustiveness-checking.md @@ -0,0 +1,30 @@ +--- +title: Exhaustiveness checking +sidebar: + order: 26 + label: 26. Exhaustiveness checking +--- + + +Exhaustiveness checking is a feature in TypeScript that ensures all possible cases of a discriminated union are handled in a `switch` statement or an `if` statement. + +```typescript +type Direction = 'up' | 'down'; + +const move = (direction: Direction) => { + switch (direction) { + case 'up': + console.log('Moving up'); + break; + case 'down': + console.log('Moving down'); + break; + default: + const exhaustiveCheck: never = direction; + console.log(exhaustiveCheck); // This line will never be executed + } +}; +``` + +The `never` type is used to ensure that the default case is exhaustive and that TypeScript will raise an error if a new value is added to the Direction type without being handled in the switch statement. + diff --git a/website/src/content/docs/be-by/book/exploring-the-type-system.md b/website/src/content/docs/be-by/book/exploring-the-type-system.md new file mode 100644 index 00000000..3616873e --- /dev/null +++ b/website/src/content/docs/be-by/book/exploring-the-type-system.md @@ -0,0 +1,819 @@ +--- +title: Exploring the Type System +sidebar: + order: 9 + label: 9. Exploring the Type System +--- + + +### The TypeScript Language Service + +The TypeScript Language Service, also known as tsserver, offers various features such as error reporting, diagnostics, compile-on-save, renaming, go to definition, completion lists, signature help, and more. It is primarily used by integrated development environments (IDEs) to provide IntelliSense support. It seamlessly integrates with Visual Studio Code and is utilized by tools like Conquer of Completion (Coc). + +Developers can leverage a dedicated API and create their own custom language service plugins to enhance the TypeScript editing experience. This can be particularly useful for implementing special linting features or enabling auto-completion for a custom templating language. + + +An example of a real-world custom plugin is "typescript-styled-plugin", which provides syntax error reporting and IntelliSense support for CSS properties in styled components. + + +For more information and quick start guides, you can refer to the official TypeScript Wiki on GitHub: [https://github.com/microsoft/TypeScript/wiki/](https://github.com/microsoft/TypeScript/wiki/) + +### Structural Typing + +TypeScript is based on a structural type system. This means that the compatibility and equivalence of types are determined by the type's actual structure or definition, rather than its name or place of declaration, as in nominative type systems like C# or C. + +TypeScript's structural type system was designed based on how JavaScript's dynamic duck typing system works during runtime. + +The following example is valid TypeScript code. As you can observe, "X" and "Y" have the same member "a," even though they have different declaration names. The types are determined by their structures, and in this case, since the structures are the same, they are compatible and valid. + +```typescript +type X = { + a: string; +}; +type Y = { + a: string; +}; +const x: X = { a: 'a' }; +const y: Y = x; // Valid +``` + +### TypeScript Fundamental Comparison Rules + +The TypeScript comparison process is recursive and executed on types nested at any level. + +A type "X" is compatible with "Y" if "Y" has at least the same members as "X". + +```typescript +type X = { + a: string; +}; +const y = { a: 'A', b: 'B' }; // Valid, as it has at least the same members as X +const r: X = y; +``` + +Function parameters are compared by types, not by their names: + +```typescript +type X = (a: number) => void; +type Y = (a: number) => void; +let x: X = (j: number) => undefined; +let y: Y = (k: number) => undefined; +y = x; // Valid +x = y; // Valid +``` + +Function return types must be the same: + + +```typescript +type X = (a: number) => undefined; +type Y = (a: number) => number; +let x: X = (a: number) => undefined; +let y: Y = (a: number) => 1; +y = x; // Invalid +x = y; // Invalid +``` + +The return type of a source function must be a subtype of the return type of a target function: + + +```typescript +let x = () => ({ a: 'A' }); +let y = () => ({ a: 'A', b: 'B' }); +x = y; // Valid +y = x; // Invalid member b is missing +``` + +Discarding function parameters is allowed, as it is a common practice in JavaScript, for instance using "Array.prototype.map()": + +```typescript +[1, 2, 3].map((element, _index, _array) => element + 'x'); +``` + +Therefore, the following type declarations are completely valid: + +```typescript +type X = (a: number) => undefined; +type Y = (a: number, b: number) => undefined; +let x: X = (a: number) => undefined; +let y: Y = (a: number) => undefined; // Missing b parameter +y = x; // Valid +``` + +Any additional optional parameters of the source type are valid: + +```typescript +type X = (a: number, b?: number, c?: number) => undefined; +type Y = (a: number) => undefined; +let x: X = a => undefined; +let y: Y = a => undefined; +y = x; // Valid +x = y; //Valid +``` + +Any optional parameters of the target type without corresponding parameters in the source type are valid and not an error: + +```typescript +type X = (a: number) => undefined; +type Y = (a: number, b?: number) => undefined; +let x: X = a => undefined; +let y: Y = a => undefined; +y = x; // Valid +x = y; // Valid +``` + +The rest parameter is treated as an infinite series of optional parameters: + +```typescript +type X = (a: number, ...rest: number[]) => undefined; +let x: X = a => undefined; //valid +``` + +Functions with overloads are valid if the overload signature is compatible with its implementation signature: + + +```typescript +function x(a: string): void; +function x(a: string, b: number): void; +function x(a: string, b?: number): void { + console.log(a, b); +} +x('a'); // Valid +x('a', 1); // Valid + +function y(a: string): void; // Invalid, not compatible with implementation signature +function y(a: string, b: number): void; +function y(a: string, b: number): void { + console.log(a, b); +} +y('a'); +y('a', 1); +``` + +Function parameter comparison succeeds if the source and target parameters are assignable to supertypes or subtypes (bivariance). + +```typescript +// Supertype +class X { + a: string; + constructor(value: string) { + this.a = value; + } +} +// Subtype +class Y extends X {} +// Subtype +class Z extends X {} + +type GetA = (x: X) => string; +const getA: GetA = x => x.a; + +// Bivariance does accept supertypes +console.log(getA(new X('x'))); // Valid +console.log(getA(new Y('Y'))); // Valid +console.log(getA(new Z('z'))); // Valid +``` + +Enums are comparable and valid with numbers and vice versa, but comparing Enum values from different Enum types is invalid. + + +```typescript +enum X { + A, + B, +} +enum Y { + A, + B, + C, +} +const xa: number = X.A; // Valid +const ya: Y = 0; // Valid +X.A === Y.A; // Invalid +``` + +Instances of a class are subject to a compatibility check for their private and protected members: + + +```typescript +class X { + public a: string; + constructor(value: string) { + this.a = value; + } +} + +class Y { + private a: string; + constructor(value: string) { + this.a = value; + } +} + +let x: X = new Y('y'); // Invalid +``` + +The comparison check does not take into consideration the different inheritance hierarchy, for instance: + +```typescript +class X { + public a: string; + constructor(value: string) { + this.a = value; + } +} +class Y extends X { + public a: string; + constructor(value: string) { + super(value); + this.a = value; + } +} +class Z { + public a: string; + constructor(value: string) { + this.a = value; + } +} +let x: X = new X('x'); +let y: Y = new Y('y'); +let z: Z = new Z('z'); +x === y; // Valid +x === z; // Valid even if z is from a different inheritance hierarchy +``` + +Generics are compared using their structures based on the resulting type after applying the generic parameter, only the final result is compared as a non-generic type. + + +```typescript +interface X { + a: T; +} +let x: X = { a: 1 }; +let y: X = { a: 'a' }; +x === y; // Invalid as the type argument is used in the final structure +``` + +```typescript +interface X {} +const x: X = 1; +const y: X = 'a'; +x === y; // Valid as the type argument is not used in the final structure +``` + +When generics do not have their type argument specified, all the unspecified arguments are treated as types with "any": + +```typescript +type X = (x: T) => T; +type Y = (y: K) => K; +let x: X = x => x; +let y: Y = y => y; +x = y; // Valid +``` + +Remember: + + +```typescript +let a: number = 1; +let b: number = 2; +a = b; // Valid, everything is assignable to itself + +let c: any; +c = 1; // Valid, all types are assignable to any + +let d: unknown; +d = 1; // Valid, all types are assignable to unknown + +let e: unknown; +let e1: unknown = e; // Valid, unknown is only assignable to itself and any +let e2: any = e; // Valid +let e3: number = e; // Invalid + +let f: never; +f = 1; // Invalid, nothing is assignable to never + +let g: void; +let g1: any; +g = 1; // Invalid, void is not assignable to or from anything expect any +g = g1; // Valid +``` + +Please note that when "strictNullChecks" is enabled, "null" and "undefined" are treated similarly to "void"; otherwise, they are similar to "never". + +### Types as Sets + +In TypeScript, a type is a set of possible values. This set is also referred to as the domain of the type. Each value of a type can be viewed as an element in a set. A type establishes the constraints that every element in the set must satisfy to be considered a member of that set. +The primary task of TypeScript is to check and verify whether one set is a subset of another. + +TypeScript supports various types of sets: + +| Set term | TypeScript | Notes | +| ------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------ | +| Empty set | never | "never" contains anything apart itself | +| Single element set | undefined / null / literal type | | +| Finite set | boolean / union | | +| Infinite set | string / number / object | | +| Universal set | any / unknown | Every element is a member of "any" and every set is a subset of it / "unknown" is a type-safe counterpart of "any" | + +Here few examples: + +| TypeScript | Set term | Example | +| --------------------- | ---------------------- | ------------------------------------------------------------------------------- | +| never | ∅ (empty set) | const x: never = 'x'; // Error: Type 'string' is not assignable to type 'never' | +| | | +| Literal type | Single element set | type X = 'X'; | +| | | type Y = 7; | +| | | +| Value assignable to T | Value ∈ T (member of) | type XY = 'X' \| 'Y'; | +| | | const x: XY = 'X'; | +| | | +| T1 assignable to T2 | T1 ⊆ T2 (subset of) | type XY = 'X' \| 'Y'; | +| | | const x: XY = 'X'; | +| | | const j: XY = 'J'; // Type '"J"' is not assignable to type 'XY'. | +| | | | +| T1 extends T2 | T1 ⊆ T2 (subset of) | type X = 'X' extends string ? true : false; | +| | | +| T1 \| T2 | T1 ∪ T2 (union) | type XY = 'X' \| 'Y'; | +| | | type JK = 1 \| 2; | +| | | +| T1 & T2 | T1 ∩ T2 (intersection) | type X = \{ a: string \} | +| | | type Y = \{ b: string \} | +| | | type XY = X & Y | +| | | const x: XY = \{ a: 'a', b: 'b' \} | +| | | +| unknown | Universal set | const x: unknown = 1 | + +An union, (T1 | T2) creates a wider set (both): + +```typescript +type X = { + a: string; +}; +type Y = { + b: string; +}; +type XY = X | Y; +const r: XY = { a: 'a', b: 'x' }; // Valid +``` + +An intersection, (T1 & T2) create a narrower set (only shared): + + +```typescript +type X = { + a: string; +}; +type Y = { + a: string; + b: string; +}; +type XY = X & Y; +const r: XY = { a: 'a' }; // Invalid +const j: XY = { a: 'a', b: 'b' }; // Valid +``` + +The `extends` keyword could be considered as a "subset of" in this context. It sets a constraint for a type. The extends used with a generic, take the generic as an infinite set and it will constrain it to a more specific type. +Please note that `extends` has nothing to do with hierarchy in a OOP sense (there is no this concept in TypeScript). +TypeScript works with sets and does not have a strict hierarchy, infact, as in the example below, two types could overlap without either being a subtype of the other type (TypeScript considers the structure, shape of the objects). + +```typescript +interface X { + a: string; +} +interface Y extends X { + b: string; +} +interface Z extends Y { + c: string; +} +const z: Z = { a: 'a', b: 'b', c: 'c' }; +interface X1 { + a: string; +} +interface Y1 { + a: string; + b: string; +} +interface Z1 { + a: string; + b: string; + c: string; +} +const z1: Z1 = { a: 'a', b: 'b', c: 'c' }; + +const r: Z1 = z; // Valid +``` + +### Assign a type: Type Declarations and Type Assertions + +A type can be assigned in different ways in TypeScript: + +#### Type Declaration + +In the following example, we use x: X (": Type") to declare a type for the variable x. + +```typescript +type X = { + a: string; +}; + +// Type declaration +const x: X = { + a: 'a', +}; +``` + +If the variable is not in the specified format, TypeScript will report an error. For instance: + + +```typescript +type X = { + a: string; +}; + +const x: X = { + a: 'a', + b: 'b', // Error: Object literal may only specify known properties +}; +``` + +#### Type Assertion + +It is possible to add an assertion by using the `as` keyword. This tells the compiler that the developer has more information about a type and silences any errors that may occur. + +For example: + +```typescript +type X = { + a: string; +}; +const x = { + a: 'a', + b: 'b', +} as X; +``` + +In the above example, the object x is asserted to have the type X using the as keyword. This informs the TypeScript compiler that the object conforms to the specified type, even though it has an additional property b not present in the type definition. + +Type assertions are useful in situations where a more specific type needs to be specified, especially when working with the DOM. For instance: + +```typescript +const myInput = document.getElementById('my_input') as HTMLInputElement; +``` + +Here, the type assertion as HTMLInputElement is used to tell TypeScript that the result of getElementById should be treated as an HTMLInputElement. +Type assertions can also be used to remap keys, as shown in the example below with template literals: + +```typescript +type J = { + [Property in keyof Type as `prefix_${string & + Property}`]: () => Type[Property]; +}; +type X = { + a: string; + b: number; +}; +type Y = J; +``` + +In this example, the type `J` uses a mapped type with a template literal to remap the keys of Type. It creates new properties with a "prefix_" added to each key, and their corresponding values are functions returning the original property values. + +It is worth noting that when using a type assertion, TypeScript will not execute excess property checking. Therefore, it is generally preferable to use a Type Declaration when the structure of the object is known in advance. + +#### Ambient Declarations + +Ambient declarations are files that describe types for JavaScript code, they have a file name format as `.d.ts.`. They are usually imported and used to annotate existing JavaScript libraries or to add types to existing JS files in your project. + +Many common libraries types can be found at: +[https://github.com/DefinitelyTyped/DefinitelyTyped/](https://github.com/DefinitelyTyped/DefinitelyTyped/) + +and can be installed using: + +```shell +npm install --save-dev @types/library-name +``` + +For your defined Ambient Declarations, you can import using the "triple-slash" reference: + + +```typescript +/// +``` + +You can use Ambient Declarations even within JavaScript files using `// @ts-check`. + +The `declare` keyword enables type definitions for existing JavaScript code without importing it, serving as a placeholder for types from another file or globally. + +### Property Checking and Excess Property Checking + +TypeScript is based on a structural type system but excess property checking is a property of TypeScript which allows it to check whether an object has the exact properties specified in the type. + +Excess Property Checking is performed when assigning object literals to variables or when passing them as arguments to the function's excess property, for instance. + + +```typescript +type X = { + a: string; +}; +const y = { a: 'a', b: 'b' }; +const x: X = y; // Valid because structural typing +const w: X = { a: 'a', b: 'b' }; // Invalid because excess property checking +``` + +### Weak Types + +A type is considered weak when it contains nothing but a set of all-optional properties: + +```typescript +type X = { + a?: string; + b?: string; +}; +``` + +TypeScript considers an error to assign anything to a weak type when there is no overlap, for instance, the following throws an error: + + +```typescript +type Options = { + a?: string; + b?: string; +}; + +const fn = (options: Options) => undefined; + +fn({ c: 'c' }); // Invalid +``` + +Although not recommended, if needed, it is possible to bypass this check by using type assertion: + +```typescript +type Options = { + a?: string; + b?: string; +}; +const fn = (options: Options) => undefined; +fn({ c: 'c' } as Options); // Valid +``` + +Or by adding `unknown` to the index signature to the weak type: + +```typescript +type Options = { + [prop: string]: unknown; + a?: string; + b?: string; +}; + +const fn = (options: Options) => undefined; +fn({ c: 'c' }); // Valid +``` + +### Strict Object Literal Checking (Freshness) + +Strict object literal checking, sometimes referred to as "freshness", is a feature in TypeScript that helps catch excess or misspelled properties that would otherwise go unnoticed in normal structural type checks. + +When creating an object literal, the TypeScript compiler considers it "fresh." If the object literal is assigned to a variable or passed as a parameter, TypeScript will throw an error if the object literal specifies properties that do not exist in the target type. + +However, "freshness" disappears when an object literal is widened or a type assertion is used. + +Here are some examples to illustrate: + + +```typescript +type X = { a: string }; +type Y = { a: string; b: string }; + +let x: X; +x = { a: 'a', b: 'b' }; // Freshness check: Invalid assignment +var y: Y; +y = { a: 'a', bx: 'bx' }; // Freshness check: Invalid assignment + +const fn = (x: X) => console.log(x.a); + +fn(x); +fn(y); // Widening: No errors, structurally type compatible + +fn({ a: 'a', bx: 'b' }); // Freshness check: Invalid argument + +let c: X = { a: 'a' }; +let d: Y = { a: 'a', b: '' }; +c = d; // Widening: No Freshness check +``` + +### Type Inference + +TypeScript can infer types when no annotation is provided during: + +* Variable initialization. +* Member initialization. +* Setting defaults for parameters. +* Function return type. + +For example: + +```typescript +let x = 'x'; // The type inferred is string +``` + +The TypeScript compiler analyzes the value or expression and determines its type based on the available information. + +### More Advanced Inferences + +When multiple expressions are used in type inference, TypeScript looks for the "best common types." For instance: + +```typescript +let x = [1, 'x', 1, null]; // The type inferred is: (string | number | null)[] +``` + +If the compiler cannot find the best common types, it returns a union type. For example: + +```typescript +let x = [new RegExp('x'), new Date()]; // Type inferred is: (RegExp | Date)[] +``` + +TypeScript utilizes "contextual typing" based on the variable's location to infer types. In the following example, the compiler knows that `e` is of type `MouseEvent` because of the `click` event type defined in the lib.d.ts file, which contains ambient declarations for various common JavaScript constructs and the DOM: + +```typescript +window.addEventListener('click', function (e) {}); // The inferred type of e is MouseEvent +``` + +### Type Widening + +Type widening is the process in which TypeScript assigns a type to a variable initialized when no type annotation was provided. It allows narrow to wider types but not vice versa. +In the following example: + + +```typescript +let x = 'x'; // TypeScript infers as string, a wide type +let y: 'y' | 'x' = 'y'; // y types is a union of literal types +y = x; // Invalid Type 'string' is not assignable to type '"x" | "y"'. +``` + +TypeScript assigns `string` to `x` based on the single value provided during initialization (`x`), this is an example of widening. + +TypeScript provides ways to have control of the widening process, for instance using "const". + +### Const + +Using the `const` keyword when declaring a variable results in a narrower type inference in TypeScript. + +For example: + +```typescript +const x = 'x'; // TypeScript infers the type of x as 'x', a narrower type +let y: 'y' | 'x' = 'y'; +y = x; // Valid: The type of x is inferred as 'x' +``` + +By using `const` to declare the variable x, its type is narrowed to the specific literal value 'x'. Since the type of x is narrowed, it can be assigned to the variable y without any error. +The reason the type can be inferred is because `const` variables cannot be reassigned, so their type can be narrowed down to a specific literal type, in this case, the literal type 'x'. + +#### Const Modifier on Type Parameters + +From version 5.0 of TypeScript, it is possible to specify the `const` attribute on a generic type parameter. This allows for inferring the most precise type possible. Let's see an example without using `const`: + +```typescript +function identity(value: T) { + // No const here + return value; +} +const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: string; b: string; } +``` + +As you can see, the properties `a` and `b` are inferred with a type of `string` . + +Now, let's see the difference with the `const` version: + +```typescript +function identity(value: T) { + // Using const modifier on type parameters + return value; +} +const values = identity({ a: 'a', b: 'b' }); // Type infered is: { a: "a"; b: "b"; } +``` + +Now we can see that the properties `a` and `b` are inferred as `const`, so `a` and `b` are treated as string literals rather than just `string` types. + +#### Const assertion + +This feature allows you to declare a variable with a more precise literal type based on its initialization value, signifying to the compiler that the value should be treated as an immutable literal. Here are a few examples: + +On a single property: + +```typescript +const v = { + x: 3 as const, +}; +v.x = 3; +``` + +On an entire object: + +```typescript +const v = { + x: 1, + y: 2, +} as const; +``` + +This can be particularly useful when defining the type for a tuple: + +```typescript +const x = [1, 2, 3]; // number[] +const y = [1, 2, 3] as const; // Tuple of readonly [1, 2, 3] +``` + +### Explicit Type Annotation + +We can be specific and pass a type, in the following example property `x` is of type `number`: + +```typescript +const v = { + x: 1, // Inferred type: number (widening) +}; +v.x = 3; // Valid +``` + +We can make the type annotation more specific by using a union of literal types: + + +```typescript +const v: { x: 1 | 2 | 3 } = { + x: 1, // x is now a union of literal types: 1 | 2 | 3 +}; +v.x = 3; // Valid +v.x = 100; // Invalid +``` + +### Type Narrowing + +Type Narrowing is the process in TypeScript where a general type is narrowed down to a more specific type. This occurs when TypeScript analyzes the code and determines that certain conditions or operations can refine the type information. + +Narrowing types can occur in different ways, including: + +#### Conditions + +By using conditional statements, such as `if` or `switch`, TypeScript can narrow down the type based on the outcome of the condition. For example: + +```typescript +let x: number | undefined = 10; + +if (x !== undefined) { + x += 100; // The type is number, which had been narrowed by the condition +} +``` + +#### Throwing or returning + +Throwing an error or returning early from a branch can be used to help TypeScript narrow down a type. For example: + +```typescript +let x: number | undefined = 10; + +if (x === undefined) { + throw 'error'; +} +x += 100; +``` + +Other ways to narrow down types in TypeScript include: + +* `instanceof` operator: Used to check if an object is an instance of a specific class. +* `in` operator: Used to check if a property exists in an object. +* `typeof` operator: Used to check the type of a value at runtime. +* Built-in functions like `Array.isArray()`: Used to check if a value is an array. + +#### Discriminated Union + +Using a "Discriminated Union" is a pattern in TypeScript where an explicit "tag" is added to objects to distinguish between different types within a union. This pattern is also referred to as a "tagged union." In the following example, the "tag" is represented by the property "type": + +```typescript +type A = { type: 'type_a'; value: number }; +type B = { type: 'type_b'; value: string }; + +const x = (input: A | B): string | number => { + switch (input.type) { + case 'type_a': + return input.value + 100; // type is A + case 'type_b': + return input.value + 'extra'; // type is B + } +}; +``` + +#### User-Defined Type Guards + +In cases where TypeScript is unable to determine a type, it is possible to write a helper function known as a "user-defined type guard." In the following example, we will utilize a Type Predicate to narrow down the type after applying certain filtering: + +```typescript +const data = ['a', null, 'c', 'd', null, 'f']; + +const r1 = data.filter(x => x != null); // The type is (string | null)[], TypeScript was not able to infer the type properly + +const isValid = (item: string | null): item is string => item !== null; // Custom type guard + +const r2 = data.filter(isValid); // The type is fine now string[], by using the predicate type guard we were able to narrow the type +``` + diff --git a/website/src/content/docs/be-by/book/extending-types.md b/website/src/content/docs/be-by/book/extending-types.md new file mode 100644 index 00000000..4664bc85 --- /dev/null +++ b/website/src/content/docs/be-by/book/extending-types.md @@ -0,0 +1,56 @@ +--- +title: Extending Types +sidebar: + order: 15 + label: 15. Extending Types +--- + + +It is possible to extend an `interface` (copy members from another type): + +```typescript +interface X { + a: string; +} +interface Y extends X { + b: string; +} +``` + +It is also possible to extend from multiple types: + +```typescript +interface A { + a: string; +} +interface B { + b: string; +} +interface Y extends A, B { + y: string; +} +``` + +The `extends` keyword works only on interfaces and classes, for types use an intersection: + +```typescript +type A = { + a: number; +}; +type B = { + b: number; +}; +type C = A & B; +``` + +It is possible to extend a type using an inference but not vice versa: + +```typescript +type A = { + a: string; +}; +interface B extends A { + b: string; +} +``` + diff --git a/website/src/content/docs/be-by/book/fixed-length-tuple.md b/website/src/content/docs/be-by/book/fixed-length-tuple.md new file mode 100644 index 00000000..c106ad48 --- /dev/null +++ b/website/src/content/docs/be-by/book/fixed-length-tuple.md @@ -0,0 +1,18 @@ +--- +title: Fixed Length Tuple +sidebar: + order: 30 + label: 30. Fixed Length Tuple +--- + + +A Fixed Length Tuple is a specific type of tuple that enforces a fixed number of elements of specific types, and disallows any modifications to the length of the tuple once it is defined. + +Fixed Length Tuples are useful when you need to represent a collection of values with a specific number of elements and specific types, and you want to ensure that the length and types of the tuple cannot be changed inadvertently. + + +```typescript +const x = [10, 'hello'] as const; +x.push(2); // Error +``` + diff --git a/website/src/content/docs/be-by/book/generics.md b/website/src/content/docs/be-by/book/generics.md new file mode 100644 index 00000000..0a85e8ae --- /dev/null +++ b/website/src/content/docs/be-by/book/generics.md @@ -0,0 +1,105 @@ +--- +title: Generics +sidebar: + order: 55 + label: 55. Generics +--- + + +Generics allow you to create reusable components and functions that can work with multiple types. With generics, you can parameterize types, functions, and interfaces, allowing them to operate on different types without explicitly specifying them beforehand. + +Generics allow you to make code more flexible and reusable. + +### Generic Type + +To define a generic type, you use angle brackets (`<>`) to specify the type parameters, for instance: + +```typescript +function identity(arg: T): T { + return arg; +} +const a = identity('x'); +const b = identity(123); + +const getLen = (data: ReadonlyArray) => data.length; +const len = getLen([1, 2, 3]); +``` + +### Generic Classes + +Generics can be applied also to classes, in this way they can work with multiple types by using type parameters. This is useful to create reusable class definitions that can operate on different data types while maintaining type safety. + +```typescript +class Container { + private item: T; + + constructor(item: T) { + this.item = item; + } + + getItem(): T { + return this.item; + } +} + +const numberContainer = new Container(123); +console.log(numberContainer.getItem()); // 123 + +const stringContainer = new Container('hello'); +console.log(stringContainer.getItem()); // hello +``` + +### Generic Constraints + +Generic parameters can be constrained using the `extends` keyword followed by a type or interface that the type parameter must satisfy. + +In the following example T it is must containing a properly `length` in order to be valid: + + +```typescript +const printLen = (value: T): void => { + console.log(value.length); +}; + +printLen('Hello'); // 5 +printLen([1, 2, 3]); // 3 +printLen({ length: 10 }); // 10 +printLen(123); // Invalid +``` + +An interesting feature of generic introduced in version 3.4 RC is Higher order function type inference which introduced propagated generic type arguments: + +```typescript +declare function pipe( + ab: (...args: A) => B, + bc: (b: B) => C +): (...args: A) => C; + +declare function list(a: T): T[]; +declare function box(x: V): { value: V }; + +const listBox = pipe(list, box); // (a: T) => { value: T[] } +const boxList = pipe(box, list); // (x: V) => { value: V }[] +``` + +This functionality allows more easily typed safe pointfree style programming which is common in functional programming. + +### Generic contextual narrowing + +Contextual narrowing for generics is the mechanism in TypeScript that allows the compiler to narrow down the type of a generic parameter based on the context in which it is used, it is useful when working with generic types in conditional statements: + +```typescript +function process(value: T): void { + if (typeof value === 'string') { + // Value is narrowed down to type 'string' + console.log(value.length); + } else if (typeof value === 'number') { + // Value is narrowed down to type 'number' + console.log(value.toFixed(2)); + } +} + +process('hello'); // 5 +process(3.14159); // 3.14 +``` + diff --git a/website/src/content/docs/be-by/book/getting-started-with-typescript.md b/website/src/content/docs/be-by/book/getting-started-with-typescript.md new file mode 100644 index 00000000..f84be7d5 --- /dev/null +++ b/website/src/content/docs/be-by/book/getting-started-with-typescript.md @@ -0,0 +1,189 @@ +--- +title: Getting Started With TypeScript +sidebar: + order: 8 + label: 8. Getting Started With TypeScript +--- + + +### Installation + +Visual Studio Code provides excellent support for the TypeScript language but does not include the TypeScript compiler. To install the TypeScript compiler, you can use a package manager like npm or yarn: + +```shell +npm install typescript --save-dev +``` + +or + +```shell +yarn add typescript --dev +``` + +Make sure to commit the generated lockfile to ensure that every team member uses the same version of TypeScript. + +To run the TypeScript compiler, you can use the following commands + +```shell +npx tsc +``` + +or + +```shell +yarn tsc +``` + +It is recommended to install TypeScript project-wise rather than globally, as it provides a more predictable build process. However, for one-off occasions, you can use the following command: + +```shell +npx tsc +``` + +or installing it globally: + +```shell +npm install -g typescript +``` + +If you are using Microsoft Visual Studio, you can obtain TypeScript as a package in NuGet for your MSBuild projects. In the NuGet Package Manager Console, run the following command: + +```shell +Install-Package Microsoft.TypeScript.MSBuild +``` + +During the TypeScript installation, two executables are installed: "tsc" as the TypeScript compiler and "tsserver" as the TypeScript standalone server. The standalone server contains the compiler and language services that can be utilized by editors and IDEs to provide intelligent code completion. + +Additionally, there are several TypeScript-compatible transpilers available, such as Babel (via a plugin) or swc. These transpilers can be used to convert TypeScript code into other target languages or versions. + +### Configuration + +TypeScript can be configured using the tsc CLI options or by utilizing a dedicated configuration file called tsconfig.json placed in the root of the project. + +To generate a tsconfig.json file prepopulated with recommended settings, you can use the following command: + +```shell +tsc --init +``` + +When executing the `tsc` command locally, TypeScript will compile the code using the configuration specified in the nearest tsconfig.json file. + +Here are some examples of CLI commands that run with the default settings: + +```shell +tsc main.ts // Compile a specific file (main.ts) to JavaScript +tsc src/*.ts // Compile any .ts files under the 'src' folder to JavaScript +tsc app.ts util.ts --outfile index.js // Compile two TypeScript files (app.ts and util.ts) into a single JavaScript file (index.js) +``` + +### TypeScript Configuration File + +A tsconfig.json file is used to configure the TypeScript Compiler (tsc). Usually, it is added to the root of the project, together with the `package.json` file. + +Notes: + +* tsconfig.json accepts comments even if it is in json format. +* It is advisable to use this configuration file instead of the command-line options. + +At the following link you can find the complete documentation and its schema: + +[https://www.typescriptlang.org/tsconfig](https://www.typescriptlang.org/tsconfig) + +[https://www.typescriptlang.org/tsconfig/](https://www.typescriptlang.org/tsconfig/) + +The following represents a list of the common and useful configurations: + +#### target + +The "target" property is used to specify which version of JavaScript ECMAScript version your TypeScript should emit/compile into. For modern browsers ES6 is a good option, for older browsers, ES5 is recommended. + +#### lib + +The "lib" property is used to specify which library files to include at compilation time. TypeScript automatically includes APIs for features specified in the "target" property, but it is possible to omit or pick specific libraries for particular needs. For instance, if you are working on a server project, you could exclude the "DOM" library, which is useful only in a browser environment. + +#### strict + +The "strict" property enables stronger guarantees and enhances type safety. It is advisable to always include this property in your project's tsconfig.json file. Enabling the "strict" property allows TypeScript to: + +* Emit code using "use strict" for each source file. +* Consider "null" and "undefined" in the type checking process. +* Disable the usage of the "any" type when no type annotations are present. +* Raise an error on the usage of the "this" expression, which would otherwise imply the "any" type. + +#### module + +The "module" property sets the module system supported for the compiled program. During runtime, a module loader is used to locate and execute dependencies based on the specified module system. + +The most common module loaders used in JavaScript are Node.js CommonJS for server-side applications and RequireJS for AMD modules in browser-based web applications. TypeScript can emit code for various module systems, including UMD, System, ESNext, ES2015/ES6, and ES2020. + +Note: The module system should be chosen based on the target environment and the module loading mechanism available in that environment. + +#### moduleResolution + +The "moduleResolution" property specifies the module resolution strategy. Use "node" for modern TypeScript code, the "classic" strategy is used only for old versions of TypeScript (before 1.6). + +#### esModuleInterop + +The "esModuleInterop" property allows import default from CommonJS modules that did not export using the "default" property, this property provides a shim to ensure compatibility in the emitted JavaScript. After enabling this option we can use `import MyLibrary from "my-library"` instead of `import * as MyLibrary from "my-library"`. + +#### jsx + +The "jsx" property applies only to .tsx files used in ReactJS and controls how JSX constructs are compiled into JavaScript. A common option is "preserve" which will compile to a .jsx file keeping unchanged the JSX so it can be passed to different tools like Babel for further transformations. + +#### skipLibCheck + +The "skipLibCheck'' property will prevent TypeScript from type-checking the entire imported third-party packages. This property will reduce the compile time of a project. TypeScript will still check your code against the type definitions provided by these packages. + +#### files + +The "files" property indicates to the compiler a list of files that must always be included in the program. + +#### include + + +The "include" property indicates to the compiler a list of files that we would like to include. This property allows glob-like patterns, such as "\*_" for any subdirectory, "_" for any file name, and "?" for optional characters. + + +#### exclude + +The "exclude" property indicates to the compiler a list of files that should not be included in the compilation. This can include files such as "node_modules" or test files. +Note: tsconfig.json allows comments. + +### importHelpers + +TypeScript uses helper code when generating code for certain advanced or down-leveled JavaScript features. By default, these helpers are duplicated in files using them. The `importHelpers` option imports these helpers from the `tslib` module instead, making the JavaScript output more efficient. + +### Migration to TypeScript Advice + +For large projects, it is recommended to adopt a gradual transition where TypeScript and JavaScript code will initially coexist. Only small projects can be migrated to TypeScript in one go. + +The first step of this transition is to introduce TypeScript into the build chain process. This can be done by using the "allowJs" compiler option, which permits .ts and .tsx files to coexist with existing JavaScript files. As TypeScript will fall back to a type of "any" for a variable when it cannot infer the type from JavaScript files, it is recommended to disable "noImplicitAny" in your compiler options at the beginning of the migration. + +The second step is to ensure that your JavaScript tests work alongside TypeScript files so that you can run tests as you convert each module. If you are using Jest, consider using `ts-jest`, which allows you to test TypeScript projects with Jest. + +The third step is to include type declarations for third-party libraries in your project. These declarations can be found either bundled or on DefinitelyTyped. You can search for them using [https://www.typescriptlang.org/dt/search](https://www.typescriptlang.org/dt/search) and install them using: + +```shell +npm install --save-dev @types/package-name +``` + +or + +```shell +yarn add --dev @types/package-name. +``` + +The fourth step is to migrate module by module with a bottom-up approach, following your Dependency Graph starting with the leaves. The idea is to start converting Modules that do not depend on other Modules. To visualize the dependency graphs, you can use the "madge" tool. + +Good candidate modules for these initial conversions are utility functions and code related to external APIs or specifications. It is possible to automatically generate TypeScript type definitions from Swagger contracts, GraphQL or JSON schemas to be included in your project. + +When there are no specifications or official schemas available, you can generate types from raw data, such as JSON returned by a server. However, it is recommended to generate types from specifications instead of data to avoid missing edge cases. + +During the migration, refrain from code refactoring and focus only on adding types to your modules. + +The fifth step is to enable "noImplicitAny," which will enforce that all types are known and defined, providing a better TypeScript experience for your project. + +During the migration, you can use the `@ts-check` directive, which enables TypeScript type checking in a JavaScript file. This directive provides a loose version of type checking and can be initially used to identify issues in JavaScript files. When `@ts-check` is included in a file, TypeScript will try to deduce definitions using JSDoc-style comments. However, consider using JSDoc annotations only at a very early stage of the migration. + +Consider keeping the default value of `noEmitOnError` in your tsconfig.json as false. This will allow you to output JavaScript source code even if errors are reported. + diff --git a/website/src/content/docs/be-by/book/index-signatures.md b/website/src/content/docs/be-by/book/index-signatures.md new file mode 100644 index 00000000..98121318 --- /dev/null +++ b/website/src/content/docs/be-by/book/index-signatures.md @@ -0,0 +1,22 @@ +--- +title: Index Signatures +sidebar: + order: 14 + label: 14. Index Signatures +--- + + +In TypeScript we can use as index signature `string`, `number`, and `symbol`: + +```typescript +type K = { + [name: string | number]: string; +}; +const k: K = { x: 'x', 1: 'b' }; +console.log(k['x']); +console.log(k[1]); +console.log(k['1']); // Same result as k[1] +``` + +Please note that JavaScript automatically converts an index with `number` to an index with `string` so `k[1]` or `k["1"]` return the same value. + diff --git a/website/src/content/docs/be-by/book/infer-type-inference-in-conditional-types.md b/website/src/content/docs/be-by/book/infer-type-inference-in-conditional-types.md new file mode 100644 index 00000000..d55a4a10 --- /dev/null +++ b/website/src/content/docs/be-by/book/infer-type-inference-in-conditional-types.md @@ -0,0 +1,16 @@ +--- +title: infer Type Inference in Conditional Types +sidebar: + order: 41 + label: 41. infer Type Inference in Conditional Types +--- + + +The `infer`keyword is used in conditional types to infer (extract) the type of a generic parameter from a type that depends on it. This allows you to write more flexible and reusable type definitions. + +```typescript +type ElementType = T extends (infer U)[] ? U : never; +type Numbers = ElementType; // number +type Strings = ElementType; // string +``` + diff --git a/website/src/content/docs/be-by/book/interface-and-type.md b/website/src/content/docs/be-by/book/interface-and-type.md new file mode 100644 index 00000000..bf72b916 --- /dev/null +++ b/website/src/content/docs/be-by/book/interface-and-type.md @@ -0,0 +1,87 @@ +--- +title: Interface and Type +sidebar: + order: 48 + label: 48. Interface and Type +--- + + +### Common Syntax + +In TypeScript, interfaces define the structure of objects, specifying the names and types of properties or methods that an object must have. The common syntax for defining an interface in TypeScript is as follows: + + +```typescript +interface InterfaceName { + property1: Type1; + // ... + method1(arg1: ArgType1, arg2: ArgType2): ReturnType; + // ... +} +``` + +Similarly for type definition: + + +```typescript +type TypeName = { + property1: Type1; + // ... + method1(arg1: ArgType1, arg2: ArgType2): ReturnType; + // ... +}; +``` + +`interface InterfaceName` or `type TypeName`: Defines the name of the interface. +`property1`: `Type1`: Specifies the properties of the interface along with their corresponding types. Multiple properties can be defined, each separated by a semicolon. +`method1(arg1: ArgType1, arg2: ArgType2): ReturnType;`: Specifies the methods of the interface. Methods are defined with their names, followed by a parameter list in parentheses and the return type. Multiple methods can be defined, each separated by a semicolon. + +Example interface: + +```typescript +interface Person { + name: string; + age: number; + greet(): void; +} +``` + +Example of type: + +```typescript +type TypeName = { + property1: string; + method1(arg1: string, arg2: string): string; +}; +``` + +In TypeScript, types are used to define the shape of data and enforce type checking. There are several common syntaxes for defining types in TypeScript, depending on the specific use case. Here are some examples: + +### Basic Types + +```typescript +let myNumber: number = 123; // number type +let myBoolean: boolean = true; // boolean type +let myArray: string[] = ['a', 'b']; // array of strings +let myTuple: [string, number] = ['a', 123]; // tuple +``` + +### Objects and Interfaces + +```typescript +const x: { name: string; age: number } = { name: 'Simon', age: 7 }; +``` + +### Union and Intersection Types + +```typescript +type MyType = string | number; // Union type +let myUnion: MyType = 'hello'; // Can be a string +myUnion = 123; // Or a number + +type TypeA = { name: string }; +type TypeB = { age: number }; +type CombinedType = TypeA & TypeB; // Intersection type +let myCombined: CombinedType = { name: 'John', age: 25 }; // Object with both name and age properties +``` + diff --git a/website/src/content/docs/be-by/book/intersection-types.md b/website/src/content/docs/be-by/book/intersection-types.md new file mode 100644 index 00000000..c169008f --- /dev/null +++ b/website/src/content/docs/be-by/book/intersection-types.md @@ -0,0 +1,27 @@ +--- +title: Intersection Types +sidebar: + order: 32 + label: 32. Intersection Types +--- + + +An Intersection Type is a type that represents a value that has all the properties of two or more types. Intersection Types are denoted using the `&` symbol between each type. + +```typescript +type X = { + a: string; +}; + +type Y = { + b: string; +}; + +type J = X & Y; // Intersection + +const j: J = { + a: 'a', + b: 'b', +}; +``` + diff --git a/website/src/content/docs/be-by/book/introduction.md b/website/src/content/docs/be-by/book/introduction.md new file mode 100644 index 00000000..8c65272d --- /dev/null +++ b/website/src/content/docs/be-by/book/introduction.md @@ -0,0 +1,12 @@ +--- +title: Introduction +sidebar: + order: 5 + label: 5. Introduction +--- + + +Welcome to The Concise TypeScript Book! This guide equips you with essential knowledge and practical skills for effective TypeScript development. Discover key concepts and techniques to write clean, robust code. Whether you're a beginner or an experienced developer, this book serves as both a comprehensive guide and a handy reference for leveraging TypeScript's power in your projects. + +This book covers TypeScript 5.2. + diff --git a/website/src/content/docs/be-by/book/literal-inference.md b/website/src/content/docs/be-by/book/literal-inference.md new file mode 100644 index 00000000..4b25b95f --- /dev/null +++ b/website/src/content/docs/be-by/book/literal-inference.md @@ -0,0 +1,52 @@ +--- +title: Literal Inference +sidebar: + order: 17 + label: 17. Literal Inference +--- + + +Literal Inference is a feature in TypeScript that allows the type of a variable or parameter to be inferred based on its value. + +In the following example we can see that TypeScript considers `x` a literal type as the value cannot be changed any time later, when instead `y` is inferred as string as it can be modified any time later. + +```typescript +const x = 'x'; // Literal type of 'x', because this value cannot be changed +let y = 'y'; // Type string, as we can change this value +``` + +In the following example we can see that `o.x` was inferred as a `string` (and not a literal of `a`) as TypeScript considers that the value can be changed any time later. + + +```typescript +type X = 'a' | 'b'; + +let o = { + x: 'a', // This is a wider string +}; + +const fn = (x: X) => `${x}-foo`; + +console.log(fn(o.x)); // Argument of type 'string' is not assignable to parameter of type 'X' +``` + +As you can see the code throws an error when passing `o.x` to `fn` as X is a narrower type. + +We can solve this issue by using type assertion using `const` or the `X` type: + + +```typescript +let o = { + x: 'a' as const, +}; +``` + +or: + + +```typescript +let o = { + x: 'a' as X, +}; +``` + diff --git a/website/src/content/docs/be-by/book/literal-types.md b/website/src/content/docs/be-by/book/literal-types.md new file mode 100644 index 00000000..cf8490e8 --- /dev/null +++ b/website/src/content/docs/be-by/book/literal-types.md @@ -0,0 +1,27 @@ +--- +title: Literal Types +sidebar: + order: 16 + label: 16. Literal Types +--- + + +A Literal Type is a single element set from a collective type, it defines a very exact value that is a JavaScript primitive. + +Literal Types in TypeScript are numbers, strings, and booleans. + +Example of literals: + +```typescript +const a = 'a'; // String literal type +const b = 1; // Numeric literal type +const c = true; // Boolean literal type +``` + +String, Numeric, and Boolean Literal Types are used in the union, type guard, and type aliases. +In the following example you can see a type alias union, `O` can be the only value specified and not any other string: + +```typescript +type O = 'a' | 'b' | 'c'; +``` + diff --git a/website/src/content/docs/be-by/book/mapped-type-modifiers.md b/website/src/content/docs/be-by/book/mapped-type-modifiers.md new file mode 100644 index 00000000..a7011ebd --- /dev/null +++ b/website/src/content/docs/be-by/book/mapped-type-modifiers.md @@ -0,0 +1,24 @@ +--- +title: Mapped Type Modifiers +sidebar: + order: 38 + label: 38. Mapped Type Modifiers +--- + + +Mapped Type Modifiers in TypeScript enable the transformation of properties within an existing type: + +* `readonly` or `+readonly`: This renders a property in the mapped type as read-only. +* `-readonly`: This allows a property in the mapped type to be mutable. +* `?`: This designates a property in the mapped type as optional. + +Examples: + +```typescript +type ReadOnly = { readonly [P in keyof T]: T[P] }; // All properties marked as read-only + +type Mutable = { -readonly [P in keyof T]: T[P] }; // All properties marked as mutable + +type MyPartial = { [P in keyof T]?: T[P] }; // All properties marked as optional +``` + diff --git a/website/src/content/docs/be-by/book/mapped-types.md b/website/src/content/docs/be-by/book/mapped-types.md new file mode 100644 index 00000000..eb2be1ba --- /dev/null +++ b/website/src/content/docs/be-by/book/mapped-types.md @@ -0,0 +1,28 @@ +--- +title: Mapped Types +sidebar: + order: 37 + label: 37. Mapped Types +--- + + +Mapped Types in TypeScript allow you to create new types based on an existing type by transforming each property using a mapping function. By mapping existing types, you can create new types that represent the same information in a different format. To create a mapped type, you access the properties of an existing type using the `keyof` operator and then alter them to produce a new type. +In the following example: + +```typescript +type MyMappedType = { + [P in keyof T]: T[P][]; +}; +type MyType = { + foo: string; + bar: number; +}; +type MyNewType = MyMappedType; +const x: MyNewType = { + foo: ['hello', 'world'], + bar: [1, 2, 3], +}; +``` + +we define MyMappedType to map over T's properties, creating a new type with each property as an array of its original type. Using this, we create MyNewType to represent the same info as MyType, but with each property as an array. + diff --git a/website/src/content/docs/be-by/book/merging-and-extension.md b/website/src/content/docs/be-by/book/merging-and-extension.md new file mode 100644 index 00000000..22266436 --- /dev/null +++ b/website/src/content/docs/be-by/book/merging-and-extension.md @@ -0,0 +1,50 @@ +--- +title: Merging and Extension +sidebar: + order: 52 + label: 52. Merging and Extension +--- + + +Merging and extension refer to two different concepts related to working with types and interfaces. + +Merging allows you to combine multiple declarations of the same name into a single definition, for example, when you define an interface with the same name multiple times: + +```typescript +interface X { + a: string; +} + +interface X { + b: number; +} + +const person: X = { + a: 'a', + b: 7, +}; +``` + +Extension refers to the ability to extend or inherit from existing types or interfaces to create new ones. It is a mechanism to add additional properties or methods to an existing type without modifying its original definition. Example: + +```typescript +interface Animal { + name: string; + eat(): void; +} + +interface Bird extends Animal { + sing(): void; +} + +const dog: Bird = { + name: 'Bird 1', + eat() { + console.log('Eating'); + }, + sing() { + console.log('Singing'); + }, +}; +``` + diff --git a/website/src/content/docs/be-by/book/named-tuple-type-labeled.md b/website/src/content/docs/be-by/book/named-tuple-type-labeled.md new file mode 100644 index 00000000..f14f8158 --- /dev/null +++ b/website/src/content/docs/be-by/book/named-tuple-type-labeled.md @@ -0,0 +1,17 @@ +--- +title: Named Tuple Type (Labeled) +sidebar: + order: 29 + label: 29. Named Tuple Type (Labeled) +--- + + +Tuple types can include optional labels or names for each element. These labels are for readability and tooling assistance, and do not affect the operations you can perform with them. + +```typescript +type T = string; +type Tuple1 = [T, T]; +type Tuple2 = [a: T, b: T]; +type Tuple3 = [a: T, T]; // Named Tuple plus Anonymous Tuple +``` + diff --git a/website/src/content/docs/be-by/book/namespacing.md b/website/src/content/docs/be-by/book/namespacing.md new file mode 100644 index 00000000..b27a1f6a --- /dev/null +++ b/website/src/content/docs/be-by/book/namespacing.md @@ -0,0 +1,26 @@ +--- +title: Namespacing +sidebar: + order: 57 + label: 57. Namespacing +--- + + +In TypeScript, namespaces are used to organize code into logical containers, preventing naming collisions and providing a way to group related code together. +The usage of the `export` keywords allows access to the namespace in "outside" modules. + +```typescript +export namespace MyNamespace { + export interface MyInterface1 { + prop1: boolean; + } + export interface MyInterface2 { + prop2: string; + } +} + +const a: MyNamespace.MyInterface1 = { + prop1: true, +}; +``` + diff --git a/website/src/content/docs/be-by/book/narrowing.md b/website/src/content/docs/be-by/book/narrowing.md new file mode 100644 index 00000000..fe00ffe0 --- /dev/null +++ b/website/src/content/docs/be-by/book/narrowing.md @@ -0,0 +1,107 @@ +--- +title: Narrowing +sidebar: + order: 20 + label: 20. Narrowing +--- + + +TypeScript narrowing is the process of refining the type of a variable within a conditional block. This is useful when working with union types, where a variable can have more than one type. + +TypeScript recognizes several ways to narrow the type: + +### typeof type guards + +The typeof type guard is one specific type guard in TypeScript that checks the type of a variable based on its built-in JavaScript type. + +```typescript +const fn = (x: number | string) => { + if (typeof x === 'number') { + return x + 1; // x is number + } + return -1; +}; +``` + +### Truthiness narrowing + +Truthiness narrowing in TypeScript works by checking whether a variable is truthy or falsy to narrow its type accordingly. + +```typescript +const toUpperCase = (name: string | null) => { + if (name) { + return name.toUpperCase(); + } else { + return null; + } +}; +``` + +### Equality narrowing + +Equality narrowing in TypeScript works by checking whether a variable is equal to a specific value or not, to narrow its type accordingly. + +It is used in conjunction with `switch` statements and equality operators such as `===`, `!==`, `==`, and `!=` to narrow down types. + +```typescript +const checkStatus = (status: 'success' | 'error') => { + switch (status) { + case 'success': + return true; + case 'error': + return null; + } +}; +``` + +### In Operator narrowing + +The `in` Operator narrowing in TypeScript is a way to narrow the type of a variable based on whether a property exists within the variable's type. + +```typescript +type Dog = { + name: string; + breed: string; +}; + +type Cat = { + name: string; + likesCream: boolean; +}; + +const getAnimalType = (pet: Dog | Cat) => { + if ('breed' in pet) { + return 'dog'; + } else { + return 'cat'; + } +}; +``` + +### instanceof narrowing + +The `instanceof` operator narrowing in TypeScript is a way to narrow the type of a variable based on its constructor function, by checking if an object is an instance of a certain class or interface. + +```typescript +class Square { + constructor(public width: number) {} +} +class Rectangle { + constructor( + public width: number, + public height: number + ) {} +} +function area(shape: Square | Rectangle) { + if (shape instanceof Square) { + return shape.width * shape.width; + } else { + return shape.width * shape.height; + } +} +const square = new Square(5); +const rectangle = new Rectangle(5, 10); +console.log(area(square)); // 25 +console.log(area(rectangle)); // 50 +``` + diff --git a/website/src/content/docs/be-by/book/never-type.md b/website/src/content/docs/be-by/book/never-type.md new file mode 100644 index 00000000..67a28855 --- /dev/null +++ b/website/src/content/docs/be-by/book/never-type.md @@ -0,0 +1,47 @@ +--- +title: Never type +sidebar: + order: 47 + label: 47. Never type +--- + + +The `never` type represents values that never occur. It is used to denote functions or expressions that never return or throw an error. + +For instance an infinite loop: + +```typescript +const infiniteLoop = (): never => { + while (true) { + // do something + } +}; +``` + +Throwing an error: + +```typescript +const throwError = (message: string): never => { + throw new Error(message); +}; +``` + +The `never` type is useful in ensuring type safety and catching potential errors in your code. It helps TypeScript analyze and infer more precise types when used in combination with other types and control flow statements, for instance: + +```typescript +type Direction = 'up' | 'down'; +const move = (direction: Direction): void => { + switch (direction) { + case 'up': + // move up + break; + case 'down': + // move down + break; + default: + const exhaustiveCheck: never = direction; + throw new Error(`Unhandled direction: ${exhaustiveCheck}`); + } +}; +``` + diff --git a/website/src/content/docs/be-by/book/object-types.md b/website/src/content/docs/be-by/book/object-types.md new file mode 100644 index 00000000..698fe6cc --- /dev/null +++ b/website/src/content/docs/be-by/book/object-types.md @@ -0,0 +1,38 @@ +--- +title: Object Types +sidebar: + order: 27 + label: 27. Object Types +--- + + +In TypeScript, object types describe the shape of an object. They specify the names and types of the object's properties, as well as whether those properties are required or optional. + +In TypeScript, you can define object types in two primary ways: + +Interface which defines the shape of an object by specifying the names, types, and optionality of its properties. + +```typescript +interface User { + name: string; + age: number; + email?: string; +} +``` + +Type alias, similar to an interface, defines the shape of an object. However, it can also create a new custom type that is based on an existing type or a combination of existing types. This includes defining union types, intersection types, and other complex types. + +```typescript +type Point = { + x: number; + y: number; +}; +``` + +It also possible to define a type anonymously: + +```typescript +const sum = (x: { a: number; b: number }) => x.a + x.b; +console.log(sum({ a: 5, b: 1 })); +``` + diff --git a/website/src/content/docs/be-by/book/optional-properties.md b/website/src/content/docs/be-by/book/optional-properties.md new file mode 100644 index 00000000..9991df0b --- /dev/null +++ b/website/src/content/docs/be-by/book/optional-properties.md @@ -0,0 +1,27 @@ +--- +title: Optional Properties +sidebar: + order: 12 + label: 12. Optional Properties +--- + + +An object can specify Optional Properties by adding a question mark `?` to the end of the property name: + +```typescript +type X = { + a: number; + b?: number; // Optional +}; +``` + +It is possible to specify a default value when a property is optional" + +```typescript +type X = { + a: number; + b?: number; +}; +const x = ({ a, b = 100 }: X) => a + b; +``` + diff --git a/website/src/content/docs/be-by/book/others.md b/website/src/content/docs/be-by/book/others.md new file mode 100644 index 00000000..e117aca0 --- /dev/null +++ b/website/src/content/docs/be-by/book/others.md @@ -0,0 +1,952 @@ +--- +title: Others +sidebar: + order: 61 + label: 61. Others +--- + + +### Errors and Exception Handling + +TypeScript allows you to catch and handle errors using standard JavaScript error handling mechanisms: + +Try-Catch-Finally Blocks: + +```typescript +try { + // Code that might throw an error +} catch (error) { + // Handle the error +} finally { + // Code that always executes, finally is optional +} +``` + +You can also handle different types of error: + +```typescript +try { + // Code that might throw different types of errors +} catch (error) { + if (error instanceof TypeError) { + // Handle TypeError + } else if (error instanceof RangeError) { + // Handle RangeError + } else { + // Handle other errors + } +} +``` + +Custom Error Types: + +It is possible to specify more specific error by extending on the Error `class`: + +```typescript +class CustomError extends Error { + constructor(message: string) { + super(message); + this.name = 'CustomError'; + } +} + +throw new CustomError('This is a custom error.'); +``` + +### Mixin classes + +Mixin classes allow you to combine and compose behavior from multiple classes into a single class. They provide a way to reuse and extend functionality without the need for deep inheritance chains. + +```typescript +abstract class Identifiable { + name: string = ''; + logId() { + console.log('id:', this.name); + } +} +abstract class Selectable { + selected: boolean = false; + select() { + this.selected = true; + console.log('Select'); + } + deselect() { + this.selected = false; + console.log('Deselect'); + } +} +class MyClass { + constructor() {} +} + +// Extend MyClass to include the behavior of Identifiable and Selectable +interface MyClass extends Identifiable, Selectable {} + +// Function to apply mixins to a class +function applyMixins(source: any, baseCtors: any[]) { + baseCtors.forEach(baseCtor => { + Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { + let descriptor = Object.getOwnPropertyDescriptor( + baseCtor.prototype, + name + ); + if (descriptor) { + Object.defineProperty(source.prototype, name, descriptor); + } + }); + }); +} + +// Apply the mixins to MyClass +applyMixins(MyClass, [Identifiable, Selectable]); +let o = new MyClass(); +o.name = 'abc'; +o.logId(); +o.select(); +``` + +### Asynchronous Language Features + +As TypeScript is a superset of JavaScript, it has built-in asynchronous language features of JavaScript as: + +Promises: + +Promises are a way to handle asynchronous operations and their results using methods like `.then()` and `.catch()` to handle success and error conditions. + +To learn more: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) + +Async/await: + +Async/await keywords are a way to provide a more synchronous-looking syntax for working with Promises. The `async` keyword is used to define an asynchronous function, and the `await` keyword is used within an async function to pause execution until a Promise is resolved or rejected. + +To learn more: +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await) + +The following API are well supported in TypeScript: + +Fetch API: +[https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) + +Web Workers: +[https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API) + +Shared Workers: +[https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker](https://developer.mozilla.org/en-US/docs/Web/API/SharedWorker) + +WebSocket: +[https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) + +### Iterators and Generators + +Both Iterators and Generators are well supported in TypeScript. + +Iterators are objects that implement the iterator protocol, providing a way to access elements of a collection or sequence one by one. It is a structure that contains a pointer to the next element in the iteration. They have a `next()` method that returns the next value in the sequence along with a boolean indicating if the sequence is `done`. + +```typescript +class NumberIterator implements Iterable { + private current: number; + + constructor( + private start: number, + private end: number + ) { + this.current = start; + } + + public next(): IteratorResult { + if (this.current <= this.end) { + const value = this.current; + this.current++; + return { value, done: false }; + } else { + return { value: undefined, done: true }; + } + } + + [Symbol.iterator](): Iterator { + return this; + } +} + +const iterator = new NumberIterator(1, 3); + +for (const num of iterator) { + console.log(num); +} +``` + +Generators are special functions defined using the `function*` syntax that simplifies the creation of iterators. They use the `yield` keyword to define the sequence of values and automatically pause and resume execution when values are requested. + +Generators make it easier to create iterators and are especially useful for working with large or infinite sequences. + +Example: + +```typescript +function* numberGenerator(start: number, end: number): Generator { + for (let i = start; i <= end; i++) { + yield i; + } +} + +const generator = numberGenerator(1, 5); + +for (const num of generator) { + console.log(num); +} +``` + +TypeScript also supports async iterators and async Generators. + +To learn more: + +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator) + +[https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator) + +### TsDocs JSDoc Reference + +When working with a JavaScript code base, it is possible to help TypeScript to infer the right Type by using JSDoc comments with additional annotation to provide type information. + +Example: + +```typescript +/** + * Computes the power of a given number + * @constructor + * @param {number} base – The base value of the expression + * @param {number} exponent – The exponent value of the expression + */ +function power(base: number, exponent: number) { + return Math.pow(base, exponent); +} +power(10, 2); // function power(base: number, exponent: number): number +``` + +Full documentation is provided to this link: +[https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) + +From version 3.7 it is possible to generate .d.ts type definitions from JavaScript JSDoc syntax. +More information can be found here: +[https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html](https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html) + +### @types + +Packages under the @types organization are special package naming conventions used to provide type definitions for existing JavaScript libraries or modules. For instance using: + +```shell +npm install --save-dev @types/lodash +``` + +Will install the type definitions of `lodash` in your current project. + +To contribute to the type definitions of @types package, please submit a pull request to [https://github.com/DefinitelyTyped/DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped). + +### JSX + +JSX (JavaScript XML) is an extension to the JavaScript language syntax that allows you to write HTML-like code within your JavaScript or TypeScript files. It is commonly used in React to define the HTML structure. + +TypeScript extends the capabilities of JSX by providing type checking and static analysis. + +To use JSX you need to set the `jsx` compiler option in your `tsconfig.json` file. Two common configuration options: + +* "preserve": emit .jsx files with the JSX unchanged. This option tells TypeScript to keep the JSX syntax as-is and not transform it during the compilation process. You can use this option if you have a separate tool, like Babel, that handles the transformation. +* "react": enables TypeScript's built-in JSX transformation. React.createElement will be used. + +All options are available here: +[https://www.typescriptlang.org/tsconfig#jsx](https://www.typescriptlang.org/tsconfig#jsx) + +### ES6 Modules + +TypeScript does support ES6 (ECMAScript 2015) and many subsequent versions. This means you can use ES6 syntax, such as arrow functions, template literals, classes, modules, destructuring, and more. + +To enable ES6 features in your project, you can specify the `target` property in the tsconfig.json. + +A configuration example: + +```json +{ + "compilerOptions": { + "target": "es6", + "module": "es6", + "moduleResolution": "node", + "sourceMap": true, + "outDir": "dist" + }, + "include": ["src"] +} +``` + +### ES7 Exponentiation Operator + +The exponentiation (`**`) operator computes the value obtained by raising the first operand to the power of the second operand. It functions similarly to `Math.pow()`, but with the added capability of accepting BigInts as operands. +TypeScript fully supports this operator using as `target` in your tsconfig.json file `es2016` or larger version. + +```typescript +console.log(2 ** (2 ** 2)); // 16 +``` + +### The for-await-of Statement + +This is a JavaScript feature fully supported in TypeScript which allows you to iterate over asynchronous iterable objects from target version es2018. + +```typescript +async function* asyncNumbers(): AsyncIterableIterator { + yield Promise.resolve(1); + yield Promise.resolve(2); + yield Promise.resolve(3); +} + +(async () => { + for await (const num of asyncNumbers()) { + console.log(num); + } +})(); +``` + +### New target meta-property + +You can use in TypeScript the `new.target` meta-property which enables you to determine if a function or constructor was invoked using the new operator. It allows you to detect whether an object was created as a result of a constructor call. + +```typescript +class Parent { + constructor() { + console.log(new.target); // Logs the constructor function used to create an instance + } +} + +class Child extends Parent { + constructor() { + super(); + } +} + +const parentX = new Parent(); // [Function: Parent] +const child = new Child(); // [Function: Child] +``` + +### Dynamic Import Expressions + +It is possible to conditionally load modules or lazy load them on-demand using the ECMAScript proposal for dynamic import which is supported in TypeScript. + +The syntax for dynamic import expressions in TypeScript is as follows: + + +```typescript +async function renderWidget() { + const container = document.getElementById('widget'); + if (container !== null) { + const widget = await import('./widget'); // Dynamic import + widget.render(container); + } +} + +renderWidget(); +``` + +### "tsc –watch" + +This command starts a TypeScript compiler with `--watch` parameter, with the ability to automatically recompile TypeScript files whenever they are modified. + +```shell +tsc --watch +``` + +Starting from TypeScript version 4.9, file monitoring primarily relies on file system events, automatically resorting to polling if an event-based watcher cannot be established. + +### Non-null Assertion Operator + +The Non-null Assertion Operator (Postfix !) also called Definite Assignment Assertions is a TypeScript feature that allows you to assert that a variable or property is not null or undefined, even if TypeScript's static type analysis suggests that it might be. With this feature it is possible to remove any explicit checking. + +```typescript +type Person = { + name: string; +}; + +const printName = (person?: Person) => { + console.log(`Name is ${person!.name}`); +}; +``` + +### Defaulted declarations + +Defaulted declarations are used when a variable or parameter is assigned a default value. This means that if no value is provided for that variable or parameter, the default value will be used instead. + +```typescript +function greet(name: string = 'Anonymous'): void { + console.log(`Hello, ${name}!`); +} +greet(); // Hello, Anonymous! +greet('John'); // Hello, John! +``` + +### Optional Chaining + +The optional chaining operator `?.` works like the regular dot operator (`.`) for accessing properties or methods. However, it gracefully handles null or undefined values by terminating the expression and returning `undefined`, instead of throwing an error. + +```typescript +type Person = { + name: string; + age?: number; + address?: { + street?: string; + city?: string; + }; +}; + +const person: Person = { + name: 'John', +}; + +console.log(person.address?.city); // undefined +``` + +### Nullish coalescing operator + +The nullish coalescing operator `??` returns the right-hand side value if the left-hand side is `null` or `undefined`; otherwise, it returns the left-hand side value. + +```typescript +const foo = null ?? 'foo'; +console.log(foo); // foo + +const baz = 1 ?? 'baz'; +const baz2 = 0 ?? 'baz'; +console.log(baz); // 1 +console.log(baz2); // 0 +``` + +### Template Literal Types + +Template Literal Types allow to manipulate string value at type level and generate new string types based on existing ones. They are useful to create more expressive and precise types from string-based operations. + +```typescript +type Department = 'engineering' | 'hr'; +type Language = 'english' | 'spanish'; +type Id = `${Department}-${Language}-id`; // "engineering-english-id" | "engineering-spanish-id" | "hr-english-id" | "hr-spanish-id" +``` + +### Function overloading + +Function overloading allows you to define multiple function signatures for the same function name, each with different parameter types and return type. +When you call an overloaded function, TypeScript uses the provided arguments to determine the correct function signature: + +```typescript +function makeGreeting(name: string): string; +function makeGreeting(names: string[]): string[]; + +function makeGreeting(person: unknown): unknown { + if (typeof person === 'string') { + return `Hi ${person}!`; + } else if (Array.isArray(person)) { + return person.map(name => `Hi, ${name}!`); + } + throw new Error('Unable to greet'); +} + +makeGreeting('Simon'); +makeGreeting(['Simone', 'John']); +``` + +### Recursive Types + +A Recursive Type is a type that can refer to itself. This is useful for defining data structures that have a hierarchical or recursive structure (potentially infinite nesting), such as linked lists, trees, and graphs. + +```typescript +type ListNode = { + data: T; + next: ListNode | undefined; +}; +``` + +### Recursive Conditional Types + +It is possible to define complex type relationships using logic and recursion in TypeScript. +Let’s break it down in simple terms: + +Conditional Types: allows you to define types based on boolean conditions: + +```typescript +type CheckNumber = T extends number ? 'Number' : 'Not a number'; +type A = CheckNumber<123>; // 'Number' +type B = CheckNumber<'abc'>; // 'Not a number' +``` + +Recursion: means a type definition that refers to itself within its own definition: + +```typescript +type Json = string | number | boolean | null | Json[] | { [key: string]: Json }; + +const data: Json = { + prop1: true, + prop2: 'prop2', + prop3: { + prop4: [], + }, +}; +``` + +Recursive Conditional Types combine both conditional logic and recursion. It means that a type definition can depend on itself through conditional logic, creating complex and flexible type relationships. + +```typescript +type Flatten = T extends Array ? Flatten : T; + +type NestedArray = [1, [2, [3, 4], 5], 6]; +type FlattenedArray = Flatten; // 2 | 3 | 4 | 5 | 1 | 6 +``` + +### ECMAScript Module Support in Node + +Node.js added support for ECMAScript Modules starting from version 15.3.0, and TypeScript has had ECMAScript Module Support for Node.js since version 4.7. This support can be enabled by using the `module` property with the value `nodenext` in the tsconfig.json file. Here's an example: + +```json +{ + "compilerOptions": { + "module": "nodenext", + "outDir": "./lib", + "declaration": true + } +} +``` + +Node.js supports two file extensions for modules: `.mjs` for ES modules and `.cjs` for CommonJS modules. The equivalent file extensions in TypeScript are `.mts` for ES modules and `.cts` for CommonJS modules. When the TypeScript compiler transpiles these files to JavaScript, it will create `.mjs` and `.cjs` files. + +If you want to use ES modules in your project, you can set the `type` property to "module" in your package.json file. This instructs Node.js to treat the project as an ES module project. + +Additionally, TypeScript also supports type declarations in .d.ts files. These declaration files provide type information for libraries or modules written in TypeScript, allowing other developers to utilize them with TypeScript's type checking and auto-completion features. + +### Assertion Functions + +In TypeScript, assertion functions are functions that indicate the verification of a specific condition based on their return value. In their simplest form, an assert function examines a provided predicate and raises an error when the predicate evaluates to false. + +```typescript +function isNumber(value: unknown): asserts value is number { + if (typeof value !== 'number') { + throw new Error('Not a number'); + } +} +``` + +Or can be declared as function expression: + +```typescript +type AssertIsNumber = (value: unknown) => asserts value is number; +const isNumber: AssertIsNumber = value => { + if (typeof value !== 'number') { + throw new Error('Not a number'); + } +}; +``` + +Assertion functions share similarities with type guards. Type guards were initially introduced to perform runtime checks and ensure the type of a value within a specific scope. +Specifically, a type guard is a function that evaluates a type predicate and returns a boolean value indicating whether the predicate is true or false. This differs slightly from assertion functions,where the intention is to throw an error rather than returning false when the predicate is not satisfied. + +Example of type guard: + +```typescript +const isNumber = (value: unknown): value is number => typeof value === 'number'; +``` + +### Variadic Tuple Types + +Variadic Tuple Types are a features introduces in TypeScript version 4.0, let’s start to learn them by revise what is a tuple: + +A tuple type is an array which has a defined length, and were the type of each element is known: + +```typescript +type Student = [string, number]; +const [name, age]: Student = ['Simone', 20]; +``` + +The term "variadic" means indefinite arity (accept a variable number of arguments). + +A variadic tuple is a tuple type which has all the property as before but the exact shape is not defined yet: + +```typescript +type Bar = [boolean, ...T, number]; + +type A = Bar<[boolean]>; // [boolean, boolean, number] +type B = Bar<['a', 'b']>; // [boolean, 'a', 'b', number] +type C = Bar<[]>; // [boolean, number] +``` + +In the previous code we can see that the tuple shape is defined by the `T` generic passed in. + +Variadic tuples can accept multiple generics make them very flexible: + +```typescript +type Bar = [...T, boolean, ...G]; + +type A = Bar<[number], [string]>; // [number, boolean, string] +type B = Bar<['a', 'b'], [boolean]>; // ["a", "b", boolean, boolean] +``` + +With the new variadic tuples we can use: + +* The spreads in tuple type syntax can now be generic, so we can represent higher-order operation on tuples and arrays even when we do not know the actual types we are operating over. +* The rest elements can occur anywhere in a tuple. + +Example: + +```typescript +type Items = readonly unknown[]; + +function concat( + arr1: T, + arr2: U +): [...T, ...U] { + return [...arr1, ...arr2]; +} + +concat([1, 2, 3], ['4', '5', '6']); // [1, 2, 3, "4", "5", "6"] +``` + +### Boxed types + +Boxed types refer to the wrapper objects that are used to represent primitive types as objects. These wrapper objects provide additional functionality and methods that are not available directly on the primitive values. + +When you access a method like `charAt` or `normalize` on a `string` primitive, JavaScript wraps it in a `String` object, calls the method, and then throws the object away. + +Demonstration: + +```typescript +const originalNormalize = String.prototype.normalize; +String.prototype.normalize = function () { + console.log(this, typeof this); + return originalNormalize.call(this); +}; +console.log('\u0041'.normalize()); +``` + +TypeScript represents this differentiation by providing separate types for the primitives and their corresponding object wrappers: + +* string => String +* number => Number +* boolean => Boolean +* symbol => Symbol +* bigint => BigInt + +The boxed types are usually not needed. Avoid using boxed types and instead use type for the primitives, for instance `string` instead of `String`. + +### Covariance and Contravariance in TypeScript + +Covariance and Contravariance are used to describe how relationships work when dealing with inheritance or assignment of types. + +Covariance means that a type relationship preserves the direction of inheritance or assignment, so if a type A is a subtype of type B, then an array of type A is also considered a subtype of an array of type B. The important thing to note here is that the subtype relationship is maintained this means that Covariance accept subtype but doesn't accept supertype. + +Contravariance means that a type relationship reverses the direction of inheritance or assignment, so if a type A is a subtype of type B, then an array of type B is considered a subtype of an array of type A. The subtype relationship is reversed this means that Contravariance accept supertype but doesn't accept subtype. + +Notes: Bivariance means accept both supertype & subtype. + +Example: Let's say we have a space for all animals and a separate space just for dogs. + +In Covariance, you can put all the dogs in the animals space because dogs are a type of animal. But you cannot put all the animals in the dog space because there might be other animals mixed in. + +In Contravariance, you cannot put all the animals in the dogs space because the animals space might contain other animals as well. However, you can put all the dogs in the animal space because all dogs are also animals. + + +```typescript +// Covariance example +class Animal { + name: string; + constructor(name: string) { + this.name = name; + } +} + +class Dog extends Animal { + breed: string; + constructor(name: string, breed: string) { + super(name); + this.breed = breed; + } +} + +let animals: Animal[] = []; +let dogs: Dog[] = []; + +// Covariance allows assigning subtype (Dog) array to supertype (Animal) array +animals = dogs; +dogs = animals; // Invalid: Type 'Animal[]' is not assignable to type 'Dog[]' + +// Contravariance example +type Feed = (animal: T) => void; + +let feedAnimal: Feed = (animal: Animal) => { + console.log(`Animal name: ${animal.name}`); +}; + +let feedDog: Feed = (dog: Dog) => { + console.log(`Dog name: ${dog.name}, Breed: ${dog.breed}`); +}; + +// Contravariance allows assigning supertype (Animal) callback to subtype (Dog) callback +feedDog = feedAnimal; +feedAnimal = feedDog; // Invalid: Type 'Feed' is not assignable to type 'Feed'. +``` + +In TypeScript, type relationships for arrays are covariant, while type relationships for function parameters are contravariant. This means that TypeScript exhibits both covariance and contravariance, depending on the context. + +#### Optional Variance Annotations for Type Parameters + +As of TypeScript 4.7.0, we can use the `out` and `in` keywords to be specific about Variance annotation. + +For Covariant, use the `out` keyword: + +```typescript +type AnimalCallback = () => T; // T is Covariant here +``` + +And for Contravariant, use the `in` keyword: + +```typescript +type AnimalCallback = (value: T) => void; // T is Contravariance here +``` + +### Template String Pattern Index Signatures + +Template string pattern index signatures allow us to define flexible index signatures using template string patterns. This feature enables us to create objects that can be indexed with specific patterns of string keys, providing more control and specificity when accessing and manipulating properties. + +TypeScript from version 4.4 allows index signatures for symbols and template string patterns. + +```typescript +const uniqueSymbol = Symbol('description'); + +type MyKeys = `key-${string}`; + +type MyObject = { + [uniqueSymbol]: string; + [key: MyKeys]: number; +}; + +const obj: MyObject = { + [uniqueSymbol]: 'Unique symbol key', + 'key-a': 123, + 'key-b': 456, +}; + +console.log(obj[uniqueSymbol]); // Unique symbol key +console.log(obj['key-a']); // 123 +console.log(obj['key-b']); // 456 +``` + +### The satisfies Operator + +The `satisfies` allows you to check if a given type satisfies a specific interface or condition. In other words, it ensures that a type has all the required properties and methods of a specific interface. It is a way to ensure a variable fits into a definition of a type. +Here is an example: + + +```typescript +type Columns = 'name' | 'nickName' | 'attributes'; + +type User = Record; + +// Type Annotation using `User` +const user: User = { + name: 'Simone', + nickName: undefined, + attributes: ['dev', 'admin'], +}; + +// In the following lines, TypeScript won't be able to infer properly +user.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. +user.nickName; // string | string[] | undefined + +// Type assertion using `as` +const user2 = { + name: 'Simon', + nickName: undefined, + attributes: ['dev', 'admin'], +} as User; + +// Here too, TypeScript won't be able to infer properly +user2.attributes?.map(console.log); // Property 'map' does not exist on type 'string | string[]'. Property 'map' does not exist on type 'string'. +user2.nickName; // string | string[] | undefined + +// Using `satisfies` operators we can properly infer the types now +const user3 = { + name: 'Simon', + nickName: undefined, + attributes: ['dev', 'admin'], +} satisfies User; + +user3.attributes?.map(console.log); // TypeScript infers correctly: string[] +user3.nickName; // TypeScript infers correctly: undefined +``` + +### Type-Only Imports and Export + +Type-Only Imports and Export allows you to import or export types without importing or exporting the values or functions associated with those types. This can be useful for reducing the size of your bundle. + +To use type-only imports, you can use the `import type` keyword. + +TypeScript permits using both declaration and implementation file extensions (.ts, .mts, .cts, and .tsx) in type-only imports, regardless of `allowImportingTsExtensions` settings. + +For example: + + +```typescript +import type { House } from './house.ts'; +``` + +The following are supported forms: + + +```typescript +import type T from './mod'; +import type { A, B } from './mod'; +import type * as Types from './mod'; +export type { T }; +export type { T } from './mod'; +``` + +### using declaration and Explicit Resource Management + +A `using` declaration is a block-scoped, immutable binding, similar to `const`, used for managing disposable resources. When initialized with a value, the `Symbol.dispose` method of that value is recorded and subsequently executed upon exiting the enclosing block scope. + +This is based on ECMAScript's Resource Management feature, which is useful for performing essential cleanup tasks after object creation, such as closing connections, deleting files, and releasing memory. + +Notes: + +* Due to its recent introduction in TypeScript version 5.2, most runtimes lack native support. You'll need polyfills for: `Symbol.dispose`, `Symbol.asyncDispose`, `DisposableStack`, `AsyncDisposableStack`, `SuppressedError`. +* Additionally, you will need to configure your tsconfig.json as follows: + +```json +{ + "compilerOptions": { + "target": "es2022", + "lib": ["es2022", "esnext.disposable", "dom"] + } +} +``` + +Example: + + +```typescript +//@ts-ignore +Symbol.dispose ??= Symbol('Symbol.dispose'); // Simple polify + +const doWork = (): Disposable => { + return { + [Symbol.dispose]: () => { + console.log('disposed'); + }, + }; +}; + +console.log(1); + +{ + using work = doWork(); // Resource is declared + console.log(2); +} // Resource is disposed (e.g., `work[Symbol.dispose]()` is evaluated) + +console.log(3); +``` + +The code will log: + +```shell +1 +2 +disposed +3 +``` + +A resource eligible for disposal must adhere to the `Disposable` interface: + +```typescript +// lib.esnext.disposable.d.ts +interface Disposable { + [Symbol.dispose](): void; +} +``` + +The `using` declarations record resource disposal operations in a stack, ensuring they are disposed in reverse order of declaration: + + +```typescript +{ + using j = getA(), + y = getB(); + using k = getC(); +} // disposes `C`, then `B`, then `A`. +``` + +Resources are guaranteed to be disposed, even if subsequent code or exceptions occur. This may lead to disposal potentially throwing an exception, possibly suppressing another. To retain information on suppressed errors, a new native exception, `SuppressedError`, is introduced. + +#### await using declaration + +An `await using` declaration handles an asynchronously disposable resource. The value must have a `Symbol.asyncDispose` method, which will be awaited at the block's end. + + +```typescript +async function doWorkAsync() { + await using work = doWorkAsync(); // Resource is declared +} // Resource is disposed (e.g., `await work[Symbol.asyncDispose]()` is evaluated) +``` + +For an asynchronously disposable resource, it must adhere to either the `Disposable` or `AsyncDisposable` interface: + +```typescript +// lib.esnext.disposable.d.ts +interface AsyncDisposable { + [Symbol.asyncDispose](): Promise; +} +``` + + +```typescript +//@ts-ignore +Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); // Simple polify + +class DatabaseConnection implements AsyncDisposable { + // A method that is called when the object is disposed asynchronously + [Symbol.asyncDispose]() { + // Close the connection and return a promise + return this.close(); + } + + async close() { + console.log('Closing the connection...'); + await new Promise(resolve => setTimeout(resolve, 1000)); + console.log('Connection closed.'); + } +} + +async function doWork() { + // Create a new connection and dispose it asynchronously when it goes out of scope + await using connection = new DatabaseConnection(); // Resource is declared + console.log('Doing some work...'); +} // Resource is disposed (e.g., `await connection[Symbol.asyncDispose]()` is evaluated) + +doWork(); +``` + +The code logs: + +```shell +Doing some work... +Closing the connection... +Connection closed. +``` + +The `using` and `await using` declarations are allowed in Statements: `for`, `for-in`, `for-of`, `for-await-of`, `switch`. + +### Import Attributes + +TypeScript 5.3's Import Attributes (labels for imports) tell the runtime how to handle modules (JSON, etc.). This improves security by ensuring clear imports and aligns with Content Security Policy (CSP) for safer resource loading. TypeScript ensures they are valid but lets the runtime handle their interpretation for specific module handling. + +Example: + + +```typescript +import config from './config.json' with { type: 'json' }; +``` + +with dynamic import: + + +```typescript +const config = import('./config.json', { with: { type: 'json' } }); +``` diff --git a/website/src/content/docs/be-by/book/overloads.md b/website/src/content/docs/be-by/book/overloads.md new file mode 100644 index 00000000..931f4bab --- /dev/null +++ b/website/src/content/docs/be-by/book/overloads.md @@ -0,0 +1,56 @@ +--- +title: Overloads +sidebar: + order: 51 + label: 51. Overloads +--- + + +Function overloads in TypeScript allow you to define multiple function signatures for a single function name, enabling you to define functions that can be called in multiple ways. Here's an example: + +```typescript +// Overloads +function sayHi(name: string): string; +function sayHi(names: string[]): string[]; + +// Implementation +function sayHi(name: unknown): unknown { + if (typeof name === 'string') { + return `Hi, ${name}!`; + } else if (Array.isArray(name)) { + return name.map(name => `Hi, ${name}!`); + } + throw new Error('Invalid value'); +} + +sayHi('xx'); // Valid +sayHi(['aa', 'bb']); // Valid +``` + +Here's another example of using function overloads within a `class`: + +```typescript +class Greeter { + message: string; + + constructor(message: string) { + this.message = message; + } + + // overload + sayHi(name: string): string; + sayHi(names: string[]): ReadonlyArray; + + // implementation + sayHi(name: unknown): unknown { + if (typeof name === 'string') { + return `${this.message}, ${name}!`; + } else if (Array.isArray(name)) { + return name.map(name => `${this.message}, ${name}!`); + } + throw new Error('value is invalid'); + } +} +console.log(new Greeter('Hello').sayHi('Simon')); +``` + diff --git a/website/src/content/docs/be-by/book/predefined-conditional-types.md b/website/src/content/docs/be-by/book/predefined-conditional-types.md new file mode 100644 index 00000000..b909b327 --- /dev/null +++ b/website/src/content/docs/be-by/book/predefined-conditional-types.md @@ -0,0 +1,26 @@ +--- +title: Predefined Conditional Types +sidebar: + order: 42 + label: 42. Predefined Conditional Types +--- + + +In TypeScript, Predefined Conditional Types are built-in conditional types provided by the language. They are designed to perform common type transformations based on the characteristics of a given type. + +`Exclude`: This type removes all the types from Type that are assignable to ExcludedType. + +`Extract`: This type extracts all the types from Union that are assignable to Type. + +`NonNullable`: This type removes null and undefined from Type. + +`ReturnType`: This type extracts the return type of a function Type. + +`Parameters`: This type extracts the parameter types of a function Type. + +`Required`: This type makes all properties in Type required. + +`Partial`: This type makes all properties in Type optional. + +`Readonly`: This type makes all properties in Type readonly. + diff --git a/website/src/content/docs/be-by/book/primitive-types.md b/website/src/content/docs/be-by/book/primitive-types.md new file mode 100644 index 00000000..3c9e0710 --- /dev/null +++ b/website/src/content/docs/be-by/book/primitive-types.md @@ -0,0 +1,128 @@ +--- +title: Primitive Types +sidebar: + order: 10 + label: 10. Primitive Types +--- + + +TypeScript supports 7 primitive types. A primitive data type refers to a type that is not an object and does not have any methods associated with it. In TypeScript, all primitive types are immutable, meaning their values cannot be changed once they are assigned. + +### string + +The `string` primitive type stores textual data, and the value is always double or single-quoted. + +```typescript +const x: string = 'x'; +const y: string = 'y'; +``` + +Strings can span multiple lines if surrounded by the backtick (`) character: + +```typescript +let sentence: string = `xxx, + yyy`; +``` + +### boolean + +The `boolean` data type in TypeScript stores a binary value, either `true` or `false`. + +```typescript +const isReady: boolean = true; +``` + +### number + +A `number` data type in TypeScript is represented with a 64-bit floating point value. A `number` type can represent integers and fractions. +TypeScript also supports hexadecimal, binary, and octal, for instance: + +```typescript +const decimal: number = 10; +const hexadecimal: number = 0xa00d; // Hexadecimal starts with 0x +const binary: number = 0b1010; // Binary starts with 0b +const octal: number = 0o633; // Octal starts with 0o +``` + +### bigInt + +A `bigInt` represents numeric values that are very large (253 – 1) and cannot be represented with a `number`. + +A `bigInt` can be created by calling the built-in function `BigInt()` or by adding `n` to the end of any integer numeric literal: + +```typescript +const x: bigint = BigInt(9007199254740991); +const y: bigint = 9007199254740991n; +``` + +Notes: + +* `bigInt` values cannot be mixed with `number` and cannot be used with built-in `Math`, they must be coerced to the same type. +* `bigInt` values are available only if target configuration is ES2020 or higher. + +### Symbol + +Symbols are unique identifiers that can be used as property keys in objects to prevent naming conflicts. + +```typescript +type Obj = { + [sym: symbol]: number; +}; + +const a = Symbol('a'); +const b = Symbol('b'); +let obj: Obj = {}; +obj[a] = 123; +obj[b] = 456; + +console.log(obj[a]); // 123 +console.log(obj[b]); // 456 +``` + +### null and undefined + +`null` and `undefined` types both represent no value or the absence of any value. + +The `undefined` type means the value is not assigned or initialized or indicates an unintentional absence of value. + +The `null` type means that we know that the field does not have a value, so value is unavailable, it indicates an intentional absence of value. + +### Array + +An `array` is a data type that can store multiple values of the same type or not. It can be defined using the following syntax: + +```typescript +const x: string[] = ['a', 'b']; +const y: Array = ['a', 'b']; +const j: Array = ['a', 1, 'b', 2]; // Union +``` + +TypeScript supports readonly arrays using the following syntax: + + +```typescript +const x: readonly string[] = ['a', 'b']; // Readonly modifier +const y: ReadonlyArray = ['a', 'b']; +const j: ReadonlyArray = ['a', 1, 'b', 2]; +j.push('x'); // Invalid +``` + +TypeScript supports tuple and readonly tuple: + +```typescript +const x: [string, number] = ['a', 1]; +const y: readonly [string, number] = ['a', 1]; +``` + +### any + +The `any` data type represents literally "any" value, it is the default value when TypeScript cannot infer the type or is not specified. + +When using `any` TypeScript compiler skips the type checking so there is no type safety when `any` is being used. Generally do not use `any` to silence the compiler when an error occurs, instead focus on fixing the error as with using `any` it is possible to break contracts and we lose the benefits of TypeScript autocomplete. + +The `any` type could be useful during a gradual migration from JavaScript to TypeScript, as it can silence the compiler. + +For new projects use TypeScript configuration `noImplicitAny` which enables TypeScript to issue errors where `any` is used or inferred. + +The `any`type is usually a source of errors which can mask real problems with your types. Avoid using it as much as possible. + diff --git a/website/src/content/docs/be-by/book/readonly-properties.md b/website/src/content/docs/be-by/book/readonly-properties.md new file mode 100644 index 00000000..94538fac --- /dev/null +++ b/website/src/content/docs/be-by/book/readonly-properties.md @@ -0,0 +1,28 @@ +--- +title: Readonly Properties +sidebar: + order: 13 + label: 13. Readonly Properties +--- + + +Is it possible to prevent writing on a property by using the modifier `readonly`which makes sure that the property cannot be re-written but does not provide any guarantee of total immutability: + +```typescript +interface Y { + readonly a: number; +} + +type X = { + readonly a: number; +}; + +type J = Readonly<{ + a: number; +}>; + +type K = { + readonly [index: number]: string; +}; +``` + diff --git a/website/src/content/docs/be-by/book/strictnullchecks.md b/website/src/content/docs/be-by/book/strictnullchecks.md new file mode 100644 index 00000000..45134d24 --- /dev/null +++ b/website/src/content/docs/be-by/book/strictnullchecks.md @@ -0,0 +1,10 @@ +--- +title: strictNullChecks +sidebar: + order: 18 + label: 18. strictNullChecks +--- + + +`strictNullChecks` is a TypeScript compiler option that enforces strict null checking. When this option is enabled, variables and parameters can only be assigned `null` or `undefined` if they have been explicitly declared to be of that type using the union type `null` | `undefined`. If a variable or parameter is not explicitly declared as nullable, TypeScript will generate an error to prevent potential runtime errors. + diff --git a/website/src/content/docs/be-by/book/symbols.md b/website/src/content/docs/be-by/book/symbols.md new file mode 100644 index 00000000..51712f66 --- /dev/null +++ b/website/src/content/docs/be-by/book/symbols.md @@ -0,0 +1,27 @@ +--- +title: Symbols +sidebar: + order: 58 + label: 58. Symbols +--- + + +Symbols are a primitive data type that represents an immutable value which is guaranteed to be globally unique throughout the lifetime of the program. + +Symbols can be used as keys for object properties and provide a way to create non-enumerable properties. + +```typescript +const key1: symbol = Symbol('key1'); +const key2: symbol = Symbol('key2'); + +const obj = { + [key1]: 'value 1', + [key2]: 'value 2', +}; + +console.log(obj[key1]); // value 1 +console.log(obj[key2]); // value 2 +``` + +In WeakMaps and WeakSets, symbols are now permissible as keys. + diff --git a/website/src/content/docs/be-by/book/table-of-contents.md b/website/src/content/docs/be-by/book/table-of-contents.md new file mode 100644 index 00000000..430d8594 --- /dev/null +++ b/website/src/content/docs/be-by/book/table-of-contents.md @@ -0,0 +1,221 @@ +--- +title: Table of Contents +sidebar: + order: 4 + label: 4. Table of Contents +--- + + + +- The Concise TypeScript Book + - Translations + - Downloads and website + - Table of Contents + - Introduction + - About the Author + - TypeScript Introduction + - What is TypeScript? + - Why TypeScript? + - TypeScript and JavaScript + - TypeScript Code Generation + - Modern JavaScript Now (Downleveling) + - Getting Started With TypeScript + - Installation + - Configuration + - TypeScript Configuration File + - target + - lib + - strict + - module + - moduleResolution + - esModuleInterop + - jsx + - skipLibCheck + - files + - include + - exclude + - importHelpers + - Migration to TypeScript Advice + - Exploring the Type System + - The TypeScript Language Service + - Structural Typing + - TypeScript Fundamental Comparison Rules + - Types as Sets + - Assign a type: Type Declarations and Type Assertions + - Type Declaration + - Type Assertion + - Ambient Declarations + - Property Checking and Excess Property Checking + - Weak Types + - Strict Object Literal Checking (Freshness) + - Type Inference + - More Advanced Inferences + - Type Widening + - Const + - Const Modifier on Type Parameters + - Const assertion + - Explicit Type Annotation + - Type Narrowing + - Conditions + - Throwing or returning + - Discriminated Union + - User-Defined Type Guards + - Primitive Types + - string + - boolean + - number + - bigInt + - Symbol + - null and undefined + - Array + - any + - Type Annotations + - Optional Properties + - Readonly Properties + - Index Signatures + - Extending Types + - Literal Types + - Literal Inference + - strictNullChecks + - Enums + - Numeric enums + - String enums + - Constant enums + - Reverse mapping + - Ambient enums + - Computed and constant members + - Narrowing + - typeof type guards + - Truthiness narrowing + - Equality narrowing + - In Operator narrowing + - instanceof narrowing + - Assignments + - Control Flow Analysis + - Type Predicates + - Discriminated Unions + - The never Type + - Exhaustiveness checking + - Object Types + - Tuple Type (Anonymous) + - Named Tuple Type (Labeled) + - Fixed Length Tuple + - Union Type + - Intersection Types + - Type Indexing + - Type from Value + - Type from Func Return + - Type from Module + - Mapped Types + - Mapped Type Modifiers + - Conditional Types + - Distributive Conditional Types + - infer Type Inference in Conditional Types + - Predefined Conditional Types + - Template Union Types + - Any type + - Unknown type + - Void type + - Never type + - Interface and Type + - Common Syntax + - Basic Types + - Objects and Interfaces + - Union and Intersection Types + - Built-in Type Primitives + - Common Built-in JS Objects + - Overloads + - Merging and Extension + - Differences between Type and Interface + - Class + - Class Common Syntax + - Constructor + - Private and Protected Constructors + - Access Modifiers + - Get and Set + - Auto-Accessors in Classes + - this + - Parameter Properties + - Abstract Classes + - With Generics + - Decorators + - Class Decorators + - Property Decorator + - Method Decorator + - Getter and Setter Decorators + - Decorator Metadata + - Inheritance + - Statics + - Property initialization + - Method overloading + - Generics + - Generic Type + - Generic Classes + - Generic Constraints + - Generic contextual narrowing + - Erased Structural Types + - Namespacing + - Symbols + - Triple-Slash Directives + - Type Manipulation + - Creating Types from Types + - Indexed Access Types + - Utility Types + - Awaited\ + - Partial\ + - Required\ + - Readonly\ + - Record\ + - Pick\ + - Omit\ + - Exclude\ + - Extract\ + - NonNullable\ + - Parameters\ + - ConstructorParameters\ + - ReturnType\ + - InstanceType\ + - ThisParameterType\ + - OmitThisParameter\ + - ThisType\ + - Uppercase\ + - Lowercase\ + - Capitalize\ + - Uncapitalize\ + - NoInfer\ + - Others + - Errors and Exception Handling + - Mixin classes + - Asynchronous Language Features + - Iterators and Generators + - TsDocs JSDoc Reference + - @types + - JSX + - ES6 Modules + - ES7 Exponentiation Operator + - The for-await-of Statement + - New target meta-property + - Dynamic Import Expressions + - "tsc –watch" + - Non-null Assertion Operator + - Defaulted declarations + - Optional Chaining + - Nullish coalescing operator + - Template Literal Types + - Function overloading + - Recursive Types + - Recursive Conditional Types + - ECMAScript Module Support in Node + - Assertion Functions + - Variadic Tuple Types + - Boxed types + - Covariance and Contravariance in TypeScript + - Optional Variance Annotations for Type Parameters + - Template String Pattern Index Signatures + - The satisfies Operator + - Type-Only Imports and Export + - using declaration and Explicit Resource Management + - await using declaration + - Import Attributes + + diff --git a/website/src/content/docs/be-by/book/template-union-types.md b/website/src/content/docs/be-by/book/template-union-types.md new file mode 100644 index 00000000..f4feb462 --- /dev/null +++ b/website/src/content/docs/be-by/book/template-union-types.md @@ -0,0 +1,16 @@ +--- +title: Template Union Types +sidebar: + order: 43 + label: 43. Template Union Types +--- + + +Template union types can be used to merge and manipulate text inside the type system for instance: + +```typescript +type Status = 'active' | 'inactive'; +type Products = 'p1' | 'p2'; +type ProductId = `id-${Products}-${Status}`; // "id-p1-active" | "id-p1-inactive" | "id-p2-active" | "id-p2-inactive" +``` + diff --git a/website/src/content/docs/be-by/book/the-concise-typescript-book.md b/website/src/content/docs/be-by/book/the-concise-typescript-book.md new file mode 100644 index 00000000..1fa0dfb3 --- /dev/null +++ b/website/src/content/docs/be-by/book/the-concise-typescript-book.md @@ -0,0 +1,20 @@ +--- +title: The Concise TypeScript Book +sidebar: + order: 1 + label: 1. The Concise TypeScript Book +--- + + +The Concise TypeScript Book provides a comprehensive and succinct overview of TypeScript's capabilities. It offers clear explanations covering all aspects found in the latest version of the language, from its powerful type system to advanced features. Whether you're a beginner or an experienced developer, this book is an invaluable resource to enhance your understanding and proficiency in TypeScript. + +This book is completely Free and Open Source. + +I believe that high-quality technical education should be accessible to everyone, which is why I keep this book free and open. + +If the book helped you squash a bug, understand a tricky concept, or advance in your career, please consider supporting my work by paying what you want (suggested price: 15 USD) or sponsoring a coffee. Your support helps me keep the content up to date and expand it with new examples and deeper explanations. + +[![Buy Me a Coffee](https://img.shields.io/badge/buy_me_a_coffee-FFDD00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://www.buymeacoffee.com/simonepoggiali) + +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate/?business=QW82ZS956XLFY&no_recurring=0¤cy_code=EUR) + diff --git a/website/src/content/docs/be-by/book/the-never-type.md b/website/src/content/docs/be-by/book/the-never-type.md new file mode 100644 index 00000000..b1769095 --- /dev/null +++ b/website/src/content/docs/be-by/book/the-never-type.md @@ -0,0 +1,24 @@ +--- +title: The never Type +sidebar: + order: 25 + label: 25. The never Type +--- + + +When a variable is narrowed to a type that cannot contain any values, the TypeScript compiler will infer that the variable must be of the `never` type. This is because The never Type represents a value that can never be produced. + +```typescript +const printValue = (val: string | number) => { + if (typeof val === 'string') { + console.log(val.toUpperCase()); + } else if (typeof val === 'number') { + console.log(val.toFixed(2)); + } else { + // val has type never here because it can never be anything other than a string or a number + const neverVal: never = val; + console.log(`Unexpected value: ${neverVal}`); + } +}; +``` + diff --git a/website/src/content/docs/be-by/book/translations.md b/website/src/content/docs/be-by/book/translations.md new file mode 100644 index 00000000..fe2f469b --- /dev/null +++ b/website/src/content/docs/be-by/book/translations.md @@ -0,0 +1,14 @@ +--- +title: Translations +sidebar: + order: 2 + label: 2. Translations +--- + + +This book has been translated into several language versions, including: + +[Chinese](https://github.com/gibbok/typescript-book/blob/main/README-zh_CN.md) + +[Italian](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) + diff --git a/website/src/content/docs/be-by/book/triple-slash-directives.md b/website/src/content/docs/be-by/book/triple-slash-directives.md new file mode 100644 index 00000000..59db88dc --- /dev/null +++ b/website/src/content/docs/be-by/book/triple-slash-directives.md @@ -0,0 +1,33 @@ +--- +title: Triple-Slash Directives +sidebar: + order: 59 + label: 59. Triple-Slash Directives +--- + + +Triple-slash directives are special comments that provide instructions to the compiler about how to process a file. These directives begin with three consecutive slashes (`///`) and are typically placed at the top of a TypeScript file and have no effects on the runtime behavior. + +Triple-slash directives are used to reference external dependencies, specify module loading behavior, enable/disable certain compiler features, and more. Few examples: + +Referencing a declaration file: + + +```typescript +/// +``` + +Indicate the module format: + + +```typescript +/// +``` + +Enable compiler options, in the following example strict mode: + + +```typescript +/// +``` + diff --git a/website/src/content/docs/be-by/book/tuple-type-anonymous.md b/website/src/content/docs/be-by/book/tuple-type-anonymous.md new file mode 100644 index 00000000..abf732c3 --- /dev/null +++ b/website/src/content/docs/be-by/book/tuple-type-anonymous.md @@ -0,0 +1,14 @@ +--- +title: Tuple Type (Anonymous) +sidebar: + order: 28 + label: 28. Tuple Type (Anonymous) +--- + + +A Tuple Type is a type that represents an array with a fixed number of elements and their corresponding types. A tuple type enforces a specific number of elements and their respective types in a fixed order. Tuple types are useful when you want to represent a collection of values with specific types, where the position of each element in the array has a specific meaning. + +```typescript +type Point = [number, number]; +``` + diff --git a/website/src/content/docs/be-by/book/type-annotations.md b/website/src/content/docs/be-by/book/type-annotations.md new file mode 100644 index 00000000..c8ce3d2c --- /dev/null +++ b/website/src/content/docs/be-by/book/type-annotations.md @@ -0,0 +1,46 @@ +--- +title: Type Annotations +sidebar: + order: 11 + label: 11. Type Annotations +--- + + +On variables declared using `var`, `let` and `const`, it is possible to optionally add a type: + +```typescript +const x: number = 1; +``` + +TypeScript does a good job of inferring types, especially when simple one, so these declarations in most cases are not necessary. + +On functions is possible to add type annotations to parameters: + +```typescript +function sum(a: number, b: number) { + return a + b; +} +``` + +The following is an example using a anonymous functions (so called lambda function): + +```typescript +const sum = (a: number, b: number) => a + b; +``` + +These annotation can be avoided when a default value for a parameter is present: + +```typescript +const sum = (a = 10, b: number) => a + b; +``` + +Return type annotations can be added to functions: + +```typescript +const sum = (a = 10, b: number): number => a + b; +``` + +This is useful especially for more complex functions as writing expliciting the return type before an implementation can help better think about the function. + +Generally consider annotating type signatures but not the body local variables and add types always to object literals. + diff --git a/website/src/content/docs/be-by/book/type-from-func-return.md b/website/src/content/docs/be-by/book/type-from-func-return.md new file mode 100644 index 00000000..d7b5acb2 --- /dev/null +++ b/website/src/content/docs/be-by/book/type-from-func-return.md @@ -0,0 +1,14 @@ +--- +title: Type from Func Return +sidebar: + order: 35 + label: 35. Type from Func Return +--- + + +Type from Func Return refers to the ability to automatically infer the return type of a function based on its implementation. This allows TypeScript to determine the type of the value returned by the function without explicit type annotations. + +```typescript +const add = (x: number, y: number) => x + y; // TypeScript can infer that the return type of the function is a number +``` + diff --git a/website/src/content/docs/be-by/book/type-from-module.md b/website/src/content/docs/be-by/book/type-from-module.md new file mode 100644 index 00000000..72eebd21 --- /dev/null +++ b/website/src/content/docs/be-by/book/type-from-module.md @@ -0,0 +1,19 @@ +--- +title: Type from Module +sidebar: + order: 36 + label: 36. Type from Module +--- + + +Type from Module refers to the ability to use a module's exported values to automatically infer their types. When a module exports a value with a specific type, TypeScript can use that information to automatically infer the type of that value when it is imported into another module. + + +```typescript +// calc.ts +export const add = (x: number, y: number) => x + y; +// index.ts +import { add } from 'calc'; +const r = add(1, 2); // r is number +``` + diff --git a/website/src/content/docs/be-by/book/type-from-value.md b/website/src/content/docs/be-by/book/type-from-value.md new file mode 100644 index 00000000..3d9d9a07 --- /dev/null +++ b/website/src/content/docs/be-by/book/type-from-value.md @@ -0,0 +1,14 @@ +--- +title: Type from Value +sidebar: + order: 34 + label: 34. Type from Value +--- + + +Type from Value in TypeScript refers to the automatic inference of a type from a value or expression through type inference. + +```typescript +const x = 'x'; // TypeScript infers 'x' as a string literal with 'const' (immutable), but widens it to 'string' with 'let' (reassignable). +``` + diff --git a/website/src/content/docs/be-by/book/type-indexing.md b/website/src/content/docs/be-by/book/type-indexing.md new file mode 100644 index 00000000..e10d1a52 --- /dev/null +++ b/website/src/content/docs/be-by/book/type-indexing.md @@ -0,0 +1,18 @@ +--- +title: Type Indexing +sidebar: + order: 33 + label: 33. Type Indexing +--- + + +Type indexing refers to the ability to define types that can be indexed by a key that is not known in advance, using an index signature to specify the type for properties that are not explicitly declared. + +```typescript +type Dictionary = { + [key: string]: T; +}; +const myDict: Dictionary = { a: 'a', b: 'b' }; +console.log(myDict['a']); // Returns a +``` + diff --git a/website/src/content/docs/be-by/book/type-manipulation.md b/website/src/content/docs/be-by/book/type-manipulation.md new file mode 100644 index 00000000..1fe6a989 --- /dev/null +++ b/website/src/content/docs/be-by/book/type-manipulation.md @@ -0,0 +1,361 @@ +--- +title: Type Manipulation +sidebar: + order: 60 + label: 60. Type Manipulation +--- + + +### Creating Types from Types + +Is it possible to create new types composing, manipulating or transforming existing types. + +Intersection Types (`&`): + +Allow you to combine multiple types into a single type: + +```typescript +type A = { foo: number }; +type B = { bar: string }; +type C = A & B; // Intersection of A and B +const obj: C = { foo: 42, bar: 'hello' }; +``` + +Union Types (`|`): + +Allow you to define a type that can be one of several types: + +```typescript +type Result = string | number; +const value1: Result = 'hello'; +const value2: Result = 42; +``` + +Mapped Types: + +Allow you to transform the properties of an existing type to create new type: + +```typescript +type Mutable = { + readonly [P in keyof T]: T[P]; +}; +type Person = { + name: string; + age: number; +}; +type ImmutablePerson = Mutable; // Properties become read-only +``` + +Conditional types: + +Allow you to create types based on some conditions: + +```typescript +type ExtractParam = T extends (param: infer P) => any ? P : never; +type MyFunction = (name: string) => number; +type ParamType = ExtractParam; // string +``` + +### Indexed Access Types + +In TypeScript is it possible to access and manipulate the types of properties within another type using an index, `Type[Key]`. + +```typescript +type Person = { + name: string; + age: number; +}; + +type AgeType = Person['age']; // number +``` + +```typescript +type MyTuple = [string, number, boolean]; +type MyType = MyTuple[2]; // boolean +``` + +### Utility Types + +Several built-in utility types can be used to manipulate types, below a list of the most common used: + +#### Awaited\ + +Constructs a type that recursively unwraps Promise types. + +```typescript +type A = Awaited>; // string +``` + +#### Partial\ + +Constructs a type with all properties of T set to optional. + +```typescript +type Person = { + name: string; + age: number; +}; + +type A = Partial; // { name?: string | undefined; age?: number | undefined; } +``` + +#### Required\ + +Constructs a type with all properties of T set to required. + +```typescript +type Person = { + name?: string; + age?: number; +}; + +type A = Required; // { name: string; age: number; } +``` + +#### Readonly\ + +Constructs a type with all properties of T set to readonly. + + +```typescript +type Person = { + name: string; + age: number; +}; + +type A = Readonly; + +const a: A = { name: 'Simon', age: 17 }; +a.name = 'John'; // Invalid +``` + +#### Record\ + +Constructs a type with a set of properties K of type T. + +```typescript +type Product = { + name: string; + price: number; +}; + +const products: Record = { + apple: { name: 'Apple', price: 0.5 }, + banana: { name: 'Banana', price: 0.25 }, +}; + +console.log(products.apple); // { name: 'Apple', price: 0.5 } +``` + +#### Pick\ + +Constructs a type by picking the specified properties K from T. + +```typescript +type Product = { + name: string; + price: number; +}; + +type Price = Pick; // { price: number; } +``` + +#### Omit\ + +Constructs a type by omitting the specified properties K from T. + +```typescript +type Product = { + name: string; + price: number; +}; + +type Name = Omit; // { name: string; } +``` + +#### Exclude\ + +Constructs a type by excluding all values of type U from T. + +```typescript +type Union = 'a' | 'b' | 'c'; +type MyType = Exclude; // b +``` + +#### Extract\ + +Constructs a type by extracting all values of type U from T. + +```typescript +type Union = 'a' | 'b' | 'c'; +type MyType = Extract; // a | c +``` + +#### NonNullable\ + +Constructs a type by excluding null and undefined from T. + +```typescript +type Union = 'a' | null | undefined | 'b'; +type MyType = NonNullable; // 'a' | 'b' +``` + +#### Parameters\ + +Extracts the parameter types of a function type T. + +```typescript +type Func = (a: string, b: number) => void; +type MyType = Parameters; // [a: string, b: number] +``` + +#### ConstructorParameters\ + +Extracts the parameter types of a constructor function type T. + +```typescript +class Person { + constructor( + public name: string, + public age: number + ) {} +} +type PersonConstructorParams = ConstructorParameters; // [name: string, age: number] +const params: PersonConstructorParams = ['John', 30]; +const person = new Person(...params); +console.log(person); // Person { name: 'John', age: 30 } +``` + +#### ReturnType\ + +Extracts the return type of a function type T. + +```typescript +type Func = (name: string) => number; +type MyType = ReturnType; // number +``` + +#### InstanceType\ + +Extracts the instance type of a class type T. + +```typescript +class Person { + name: string; + + constructor(name: string) { + this.name = name; + } + + sayHello() { + console.log(`Hello, my name is ${this.name}!`); + } +} + +type PersonInstance = InstanceType; + +const person: PersonInstance = new Person('John'); + +person.sayHello(); // Hello, my name is John! +``` + +#### ThisParameterType\ + +Extracts the type of 'this' parameter from a function type T. + +```typescript +interface Person { + name: string; + greet(this: Person): void; +} +type PersonThisType = ThisParameterType; // Person +``` + +#### OmitThisParameter\ + +Removes the 'this' parameter from a function type T. + +```typescript +function capitalize(this: String) { + return this[0].toUpperCase + this.substring(1).toLowerCase(); +} + +type CapitalizeType = OmitThisParameter; // () => string +``` + +#### ThisType\ + +Servers as a market for a contextual `this` type. + + +```typescript +type Logger = { + log: (error: string) => void; +}; + +let helperFunctions: { [name: string]: Function } & ThisType = { + hello: function () { + this.log('some error'); // Valid as "log" is a part of "this". + this.update(); // Invalid + }, +}; +``` + +#### Uppercase\ + +Make uppercase the name of the input type T. + +```typescript +type MyType = Uppercase<'abc'>; // "ABC" +``` + +#### Lowercase\ + +Make lowercase the name of the input type T. + +```typescript +type MyType = Lowercase<'ABC'>; // "abc" +``` + +#### Capitalize\ + +Capitalize the name of the input type T. + +```typescript +type MyType = Capitalize<'abc'>; // "Abc" +``` + +#### Uncapitalize\ + +Uncapitalize the name of the input type T. + +```typescript +type MyType = Uncapitalize<'Abc'>; // "abc" +``` + +#### NoInfer\ + +NoInfer is a utility type designed to block the automatic inference of types within the scope of a generic function. + +Example: + +```typescript +// Automatic inference of types within the scope of a generic function. +function fn(x: T[], y: T) { + return x.concat(y); +} +const r = fn(['a', 'b'], 'c'); // Type here is ("a" | "b" | "c")[] +``` + +With NoInfer: + + +```typescript +// Example function that uses NoInfer to prevent type inference +function fn2(x: T[], y: NoInfer) { + return x.concat(y); +} + +const r2 = fn2(['a', 'b'], 'c'); // Error: Type Argument of type '"c"' is not assignable to parameter of type '"a" | "b"'. +``` + diff --git a/website/src/content/docs/be-by/book/type-predicates.md b/website/src/content/docs/be-by/book/type-predicates.md new file mode 100644 index 00000000..72fb3869 --- /dev/null +++ b/website/src/content/docs/be-by/book/type-predicates.md @@ -0,0 +1,22 @@ +--- +title: Type Predicates +sidebar: + order: 23 + label: 23. Type Predicates +--- + + +Type Predicates in TypeScript are functions that return a boolean value and are used to narrow the type of a variable to a more specific type. + +```typescript +const isString = (value: unknown): value is string => typeof value === 'string'; + +const foo = (bar: unknown) => { + if (isString(bar)) { + console.log(bar.toUpperCase()); + } else { + console.log('not a string'); + } +}; +``` + diff --git a/website/src/content/docs/be-by/book/typescript-introduction.md b/website/src/content/docs/be-by/book/typescript-introduction.md new file mode 100644 index 00000000..88555d21 --- /dev/null +++ b/website/src/content/docs/be-by/book/typescript-introduction.md @@ -0,0 +1,219 @@ +--- +title: TypeScript Introduction +sidebar: + order: 7 + label: 7. TypeScript Introduction +--- + + +### What is TypeScript? + +TypeScript is a strongly typed programming language that builds on JavaScript. It was originally designed by Anders Hejlsberg in 2012 and is currently developed and maintained by Microsoft as an open source project. + +TypeScript compiles to JavaScript and can be executed in any JavaScript runtime (e.g., a browser or server Node.js). + +TypeScript supports multiple programming paradigms such as functional, generic, imperative, and object-oriented. TypeScript is neither an interpreted nor a compiled language. + +### Why TypeScript? + +TypeScript is a strongly typed language that helps prevent common programming mistakes and avoid certain kinds of run-time errors before the program is executed. + +A strongly typed language allows the developer to specify various program constraints and behaviors in the data type definitions, facilitating the ability to verify the correctness of the software and prevent defects. This is especially valuable in large-scale applications. + +Some of the benefits of TypeScript: + +* Static typing, optionally strongly typed +* Type Inference +* Access to ES6 and ES7 features +* Cross-Platform and Cross-browser Compatibility +* Tooling support with IntelliSense + +### TypeScript and JavaScript + +TypeScript is written in `.ts` or `.tsx` files, while JavaScript files are written in `.js` or `.jsx`. + +Files with the extension `.tsx` or `.jsx` can contain JavaScript Syntax Extension JSX, which is used in React for UI development. + +TypeScript is a typed superset of JavaScript (ECMAScript 2015) in terms of syntax. All JavaScript code is valid TypeScript code, but the reverse is not always true. + +For instance, consider a function in a JavaScript file with the `.js` extension, such as the following: + + +```typescript +const sum = (a, b) => a + b; +``` + +The function can be converted and used in TypeScript by changing the file extension to `.ts`. However, if the same function is annotated with TypeScript types, it cannot be executed in any JavaScript runtime without compilation. The following TypeScript code will produce a syntax error if it is not compiled: + + +```typescript +const sum = (a: number, b: number): number => a + b; +``` + +TypeScript was designed to detect possible exceptions that can occur at runtime during compilation time by having the developer define the intent with type annotations. In addition, TypeScript can also catch issues if no type annotation is provided. For instance, the following code snippet does not specify any TypeScript types: + + +```typescript +const items = [{ x: 1 }, { x: 2 }]; +const result = items.filter(item => item.y); +``` + +In this case, TypeScript detects an error and reports: + +```text +Property 'y' does not exist on type '{ x: number; }'. +``` + +TypeScript's type system is largely influenced by the runtime behavior of JavaScript. For example, the addition operator (+), which in JavaScript can either perform string concatenation or numeric addition, is modeled in the same way in TypeScript: + +```typescript +const result = '1' + 1; // Result is of type string +``` + +The team behind TypeScript has made a deliberate decision to flag unusual usage of JavaScript as errors. For instance, consider the following valid JavaScript code: + + +```typescript +const result = 1 + true; // In JavaScript, the result is equal 2 +``` + +However, TypeScript throws an error: + +```text +Operator '+' cannot be applied to types 'number' and 'boolean'. +``` + +This error occurs because TypeScript strictly enforces type compatibility, and in this case, it identifies an invalid operation between a number and a boolean. + +### TypeScript Code Generation + +The TypeScript compiler has two main responsibilities: checking for type errors and compiling to JavaScript. These two processes are independent of each other. Types do not affect the execution of the code in a JavaScript runtime, as they are completely erased during compilation. TypeScript can still output JavaScript even in the presence of type errors. +Here is an example of TypeScript code with a type error: + + +```typescript +const add = (a: number, b: number): number => a + b; +const result = add('x', 'y'); // Argument of type 'string' is not assignable to parameter of type 'number'. +``` + +However, it can still produce executable JavaScript output: + + +```typescript +'use strict'; +const add = (a, b) => a + b; +const result = add('x', 'y'); // xy +``` + +It is not possible to check TypeScript types at runtime. For example: + + +```typescript +interface Animal { + name: string; +} +interface Dog extends Animal { + bark: () => void; +} +interface Cat extends Animal { + meow: () => void; +} +const makeNoise = (animal: Animal) => { + if (animal instanceof Dog) { + // 'Dog' only refers to a type, but is being used as a value here. + // ... + } +}; +``` + +As the types are erased after compilation, there is no way to run this code in JavaScript. To recognize types at runtime, we need to use another mechanism. TypeScript provides several options, with a common one being "tagged union". For example: + +```typescript +interface Dog { + kind: 'dog'; // Tagged union + bark: () => void; +} +interface Cat { + kind: 'cat'; // Tagged union + meow: () => void; +} +type Animal = Dog | Cat; + +const makeNoise = (animal: Animal) => { + if (animal.kind === 'dog') { + animal.bark(); + } else { + animal.meow(); + } +}; + +const dog: Dog = { + kind: 'dog', + bark: () => console.log('bark'), +}; +makeNoise(dog); +``` + +The property "kind" is a value that can be used at runtime to distinguish between objects in JavaScript. + +It is also possible for a value at runtime to have a type different from the one declared in the type declaration. For instance, if the developer has misinterpreted an API type and annotated it incorrectly. + +TypeScript is a superset of JavaScript, so the "class" keyword can be used as a type and value at runtime. + +```typescript +class Animal { + constructor(public name: string) {} +} +class Dog extends Animal { + constructor( + public name: string, + public bark: () => void + ) { + super(name); + } +} +class Cat extends Animal { + constructor( + public name: string, + public meow: () => void + ) { + super(name); + } +} +type Mammal = Dog | Cat; + +const makeNoise = (mammal: Mammal) => { + if (mammal instanceof Dog) { + mammal.bark(); + } else { + mammal.meow(); + } +}; + +const dog = new Dog('Fido', () => console.log('bark')); +makeNoise(dog); +``` + +In JavaScript, a "class" has a "prototype" property, and the "instanceof" operator can be used to test if the prototype property of a constructor appears anywhere in the prototype chain of an object. + +TypeScript has no effect on runtime performance, as all types will be erased. However, TypeScript does introduce some build time overhead. + +### Modern JavaScript Now (Downleveling) + +TypeScript can compile code to any released version of JavaScript since ECMAScript 3 (1999). This means that TypeScript can transpile code from the latest JavaScript features to older versions, a process known as Downleveling. This allows the usage of modern JavaScript while maintaining maximum compatibility with older runtime environments. + +It's important to note that during transpilation to an older version of JavaScript, TypeScript may generate code that could incur a performance overhead compared to native implementations. + +Here are some of the modern JavaScript features that can be used in TypeScript: + +* ECMAScript modules instead of AMD-style "define" callbacks or CommonJS "require" statements. +* Classes instead of prototypes. +* Variables declaration using "let" or "const" instead of "var". +* "for-of" loop or ".forEach" instead of the traditional "for" loop. +* Arrow functions instead of function expressions. +* Destructuring assignment. +* Shorthand property/method names and computed property names. +* Default function parameters. + +By leveraging these modern JavaScript features, developers can write more expressive and concise code in TypeScript. + diff --git a/website/src/content/docs/be-by/book/union-type.md b/website/src/content/docs/be-by/book/union-type.md new file mode 100644 index 00000000..2c875f5c --- /dev/null +++ b/website/src/content/docs/be-by/book/union-type.md @@ -0,0 +1,16 @@ +--- +title: Union Type +sidebar: + order: 31 + label: 31. Union Type +--- + + +A Union Type is a type that represents a value that can be one of several types. Union Types are denoted using the `|` symbol between each possible type. + +```typescript +let x: string | number; +x = 'hello'; // Valid +x = 123; // Valid +``` + diff --git a/website/src/content/docs/be-by/book/unknown-type.md b/website/src/content/docs/be-by/book/unknown-type.md new file mode 100644 index 00000000..b4957223 --- /dev/null +++ b/website/src/content/docs/be-by/book/unknown-type.md @@ -0,0 +1,29 @@ +--- +title: Unknown type +sidebar: + order: 45 + label: 45. Unknown type +--- + + +In TypeScript, the `unknown` type represents a value that is of an unknown type. Unlike `any` type, which allows for any type of value, `unknown` requires a type check or assertion before it can be used in a specific way so no operations are permitted on an `unknown` without first asserting or narrowing to a more specific type. + +The `unknown` type is only assignable to any type and the `unknown` type itself, it is a type-safe alternative to `any`. + + +```typescript +let value: unknown; + +let value1: unknown = value; // Valid +let value2: any = value; // Valid +let value3: boolean = value; // Invalid +let value4: number = value; // Invalid +``` + +```typescript +const add = (a: unknown, b: unknown): number | undefined => + typeof a === 'number' && typeof b === 'number' ? a + b : undefined; +console.log(add(1, 2)); // 3 +console.log(add('x', 2)); // undefined +``` + diff --git a/website/src/content/docs/be-by/book/void-type.md b/website/src/content/docs/be-by/book/void-type.md new file mode 100644 index 00000000..4c1b03df --- /dev/null +++ b/website/src/content/docs/be-by/book/void-type.md @@ -0,0 +1,16 @@ +--- +title: Void type +sidebar: + order: 46 + label: 46. Void type +--- + + +The `void` type is used to indicate that a function does not return a value. + +```typescript +const sayHello = (): void => { + console.log('Hello!'); +}; +``` + diff --git a/website/src/content/docs/book/translations.md b/website/src/content/docs/book/translations.md index fe2f469b..8f6f29a9 100644 --- a/website/src/content/docs/book/translations.md +++ b/website/src/content/docs/book/translations.md @@ -12,3 +12,5 @@ This book has been translated into several language versions, including: [Italian](https://github.com/gibbok/typescript-book/blob/main/README-it_IT.md) +[Belarusian](https://github.com/gibbok/typescript-book/blob/main/README-be_BY.md) +