Генериците и увеличаването ще ви направят TypeScript Wizard

Снимка на Михал Ломза на Unsplash

Това е втората публикация в моята серия TypeScript; първият е тук.

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

С времето научих, че винаги, когато заобикалям компилатора, създавам грешка по време на изпълнение, чакаща да се случи. Всеки път, когато напръсках по малко, за да откажа грешките, плащах за това по-късно с часове старателно отстраняване на грешки.

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

„Необходима е много храброст, за да се изправим срещу нашите врагове, но също толкова много, за да се противопоставим на нашите приятели.“ А. П. Дъмбълдор

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

Използване на Generics

Кажете, че работим с училищна база данни и сме написали хубава полезна функция, наречена getBy. За да получим студентски обект по име, можем да стартираме getBy (модел, „име“, „Хари“). Нека да разгледаме как може да изглежда това (в името на простотата, аз приложих DB модела като обикновен масив).

И така, ние имаме прилична функция, но тя няма типови пояснения и няма видове означава, че няма безопасност - така че да поправим това:

Това е много по-добре! Сега нашият компилатор знае вида на резултата и това ще ни помогне по-късно. Въпреки това, за да спечелим тази тип безопасност, ние пожертвахме повторното използване на нашата функция. Ами ако искаме да го използваме за извличане на други субекти в бъдеще? Трябва да има по-добър начин.

И има! В TypeScript, както и в други силно набрани езици, можем да използваме родови типове. Generic е като променлива, но вместо да държи стойност, съдържа определение на типа. Нека рефакторираме нашата функция да използваме общ тип T вместо Student.

Сладка! Сега нашата функция е напълно използваема и все още се радваме на безопасността на типа. Забележете как на линия 5 изрично уведомявам компилатора, че ще използвам типа Student като общ Т - направих това за по-голяма яснота, но всъщност компилаторът може да заключи това самостоятелно, така че няма да се появи в следващите примери.

Сега имаме солидна полезна функция за многократна употреба, но все пак можем да се справим по-добре. Какво се случва, ако направя печатна грешка и напиша "naem" като втория ми параметър? Ще се провали безшумно. Системата ми ще действа така, сякаш този ученик не съществува, и аз ще прекарвам часове в грешки.

За да поправя това, ще въведа нов генеричен тип P. Искам P да е ключ от тип T, така че ако използвах Student, искам P да е "име", "възраст" или "hasScar". Ето как може да се направи:

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

Но още не сме свършили. Все още има третият аргумент за нашата функция, която седи там безтипна - това е неприемливо. Досега нямахме как да знаем предварително какъв тип трябва да бъде, тъй като това зависи от всичко, което предаваме като втори аргумент. Но сега, когато имаме тип P, можем динамично да го заключим. Типът на третия аргумент е T [P], така че ако T означава Student и P означава "възраст", тогава T [P] ще бъде от тип номер.

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

Увеличаване на съществуващи типове

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

За тази демонстрация ще добавим нашата функция getBy към прототипа на Array, така че да имаме по-чист синтаксис. Дали това е добра идея или не, няма значение в този момент, тъй като това, което следваме, е да научим техниката.

Когато се опитаме да добавим функцията си към прототипа на Array, можем да видим, че компилаторът се вбесява от нас:

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

По-добър подход би бил да се увеличи типът Array, но първо, нека поговорим за това как TypeScript обработва случаи на два интерфейса с един и същи тип. Отговорът е прост - ако е възможно, той ще обедини определенията, ако не, ще повдигне грешка.

Това работи:

И това не:

Сега, когато разбираме това, се изправяме пред доста лесна задача. Всичко, което трябва да направим, е да декларираме интерфейс Array и да добавим към него функцията getBy.

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

Ако искате да разширите интерфейс на външна библиотека, вероятно ще трябва да получите достъп до пространството от имена на тази библиотека. Ето пример за това как да добавите поле за потребителски идентификатор към заявката на Express:

Така че ... това е за това. Както преди, можете да си поиграете с кода на примера, за да спечелите повече увереност.

Знаеш какво трябва да направиш сега ...

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

също не пропускайте следващата ми публикация, която се отнася до декоратори на TypeScript!