Аператары TypeScript у практыцы: матэматыка, пораўнання, логіка і трынарныя выразы
Практычны ўражэнь на аператарыях вырахунку, прызначэння, парабоўкі, логічныя аператарыя, інкременту і тэрнарныя аператарыя ў TypeScript, укладаючы таксама перакананне пра роўнасці, якое кампайлер адмовяецца прыйняць.
Калі вы вже знаеце, як TypeScript прызначае та аплявуе типы, наступны крок — выконваць якісь дзеяння з цымі значэннямі: аддаць іх, парабеліраваць, спалучаць умовы та выбіраць альтернатывы. Апэратары — это симвалы, якія выкааноўваюць гэтыя дзеяння, і большасць з іх працуюць абсолютна так сама, як у JavaScript. Аднак дзеякі з іх скрываюць деталі, якія часта ствараюць багі, такія як розніца межу слабай і строгай равнасцю, а таксама разлік межу x++ і ++x.
У гідзе рассказваецца пра арыфметычныя, прызначальныя, рэляцыйныя, логічныя, інкрементныя та дэкрементныя апэратары, а таксама трынарныя апэратары, з невеликімі прыкладамі, якія можна розглядаць без ўжывання ў якіх-небудзь програмах. Па ходу вы пазнаеце, дзе кампайлер TypeScript стварае захіст, якога няма ў звычнам JavaScript.
Спачатку кароткае адказа: калькі заявы ў адной
Перш чым розглядаць аператары, варта згадаць прыемнасць сінтаксісу, на якой спакоююцца пасляэйшыя прыклады. Звычны спосаб задання двух чисоў — адна запаведзь для кожнай зменной:
let a: number = 10;
let b: number = 20;
Одна запаведзь let таксама можа стварыць калькі зменных адразу, якщо ўсе іх роздзеліць комамі:
let a: number = 10, b: number = 20;
Оба варыянты компілююцца да аднараду; другі проста ўжоцьшы. Кожная зменная у спісе мае свою анотацыю, таму ў яных можна выкарыстоўваць разныя типы:
let a: number = 10, name: string = "John", isActive: boolean = true;
Вжываць этот варыянт трэба адзіночна. Ён добра чытаемы для пары блізкая спакоюючыхся значэнняў, як a і b, якія вжываюцца ў всім гідзе, але рэчып, які ўключае лямбда-функцыю, імя і флаг, важкае для аналізу, чым тры окалічныя декларацыі.
Арыфметычныя аператары
Это знакі, вядомыя з школьной матэматыки. Пасляэйшыя прыклады пачынаюцца з двух чисоў:
// Arithmetic operators
let a: number = 10, b: number = 20;
Кожны лінійкі выкарыстоўвае адны аператар і паказвае рэзультат у каменты:
console.log(a + b); // 30 → addition
console.log(a - b); // -10 → subtraction
console.log(a * b); // 200 → multiplication
console.log(a / b); // 0.5 → division
console.log(a % b); // 10 → modulus (remainder after division)
console.log(a ** b); // 100000000000000000000 → exponentiation (10 to the power 20)
Што робіць кожны з іх:
+дадае два операнды.-вырахоўвае разлік межы правым і левым операндамі.*паўнажывае іх./дзеліць іх без заокруглення.10 / 20насправды ўравае0.5; няма окраменай дзелення цэлах, як у дзеяных іншых мовах, таму што кожна цэлае число ў JavaScript являе сабою значэнне з плаваючай канматкой.%вяртае залишок.10 % 20ўравае10: двадцать разоў не падходзіць на дзесяць, таму весь дзесятак застаецца залишком.**паднімае левы операнд на ступень правага.10 ** 20— цэна дзесяці, паўнажыванай самой сабою двадцать разоў, што пояснюе вельмі дзьвігучы рэзультат.
Адзін застерэжны момент пры викорыстанні ** і большых значэннях: цыфры, якія перавышаюць Number.MAX_SAFE_INTEGER (прыблізна дзевяць квадрільйонаў), тырадуць точнасць цэлых лічб. 10 ** 20 выведаецца точна, але арыфметычныя вырахунакі з такімі значэннямя не ўпэўненны; калі вам патрэбны точныя большы цэлыя лічбы, викорыстоўвайце bigint.
Прызначэнне і складнае прызначэнне
Звычны знак = запішвае значэнне ў зменную. JavaScript і TypeScript таксама апускаюць складныя операторы прызначэння, якія выкаанаюць арыфметычны крок і запішваюць рэзультат у адной выразе. Пачніце з двух значэнняў:
let a: number = 100, b: number = 50;
Потым апцэнтуйце a два разы: спачатку трохі дзейснейшым спосабам, а потым за дапамогою складнага прызначэння:
console.log(a); // 100
a = a + b;
console.log(a); // 150
console.log(a += b); // 200
Паспараграф за паспараграфам: a пачынаецца з значэння 100. Зьявленне a = a + b чытае текущае значэння a, дадзеўнае яго b (50) і запісвае рэзультат 150 назад у a. Ёсць тут толькі аператар + з паканальнага раздзела.
У пасляпэльнай лініі выкарыстоўваецца a += b, якое значыць то ж самае, што і a = a + b, але запісваецца болей компактна. Паколькі a у гэты момент равнае 150, яго значэнне становіцца 200. Асацыяванне таксама ёсць выразам, значэння якога — нова зберажаная цифра, таму калі a += b пасылаецца безпосередна ў console.log, выведаецца 200. Гэта працюе, але закрыцья змены стану ўнутрь параметра функцыі ускладняе розумэнне коду, таму ў рэальным кодзе краща выканаць апдэйт на самастоятнай лініі.
Такі ж правілы дапраўнае для кожнага арыметычнага апэратара: -=, *=, /=, %= і **= — яны вырахоўваюць разлік, паляпленне, дзеўержэнне, залішак або экспаненцыю, а пасля прызначаюць рэзультат.
Апэратары стосункоў
Апэратары парабянкі спаглядаюць два значэння і завжды даўаюць булева значэння — true або false. Ёсць такія операнды:
let a: number = 10;
let b: number = 20;
А парабянкі выглядаюць так:
console.log(a > b); // false
console.log(a < b); // true
console.log(a === b); // false
console.log(a >= b); // false
console.log(a <= b); // true
console.log(a == b); // false
console.log(a != b); // true
Аператы ранжавання ўсё зрозумелы: > (больш за), < (меньш за), >= (больш за або равна) і <= (меньш за або равна). Аператы равенства ==, === і != пераканальваюць, чы роўнасць двух значэнняў, але == і === прыменяюць разныя правілы, і гэта разніца заслуговае на адзінокы роздзел. Строгі аналаг != — это !==, і яго зазвычай трэба вжываць.
Рыхлая равенасць проты строгай равенасці
Два аператы равенства разніцяюцца ў спосабе адносу да типаў:
==— это рыхлая равенасць. Якщо операнды маюць разныя типы, яна тылуносна пераканвалюе адны з іх пры парабяранні значэнняў.
=== — гэта строгая абароўненасць. Яна выкалічвае абароўненасць толькі калі типы і значэнні абоўшча адпаведныя, не выконвае ніякіх пераканвертацый і вяртае false, калі типы разныя.Ёнклюзіўна для ілустрацыі, возьмімо число і строку, якія выглядаюць аднакова. Іх усіх намеравана пазначылі як any; прычына гэтага будзе пасля.
let num1: any = 10;
let num2: any = "10";
Порэшчанне іх за дапамою кожнага аператара дае разныя рэзультаты:
console.log(num1 == num2); // true → same value, type is ignored
console.log(num1 === num2); // false → same value, but number ≠ string
num1 храніць цэлык 10, а num2 — строку "10". Аблакавая роўнасць ператварае строку на цэлык, знаходзіць 10 на обох боках і вяртае true. Стрыглая роўнасць зовсім не ператварае дадзеныя; цэлык ніколі не можа быць роўны строке, таму рэзультат — false. Пакалі правілы ператварэння аблакавай роўнасці даюць неспадзяваныя рэзультаты (напрыклад, 0 == "" ў значэнні true), большасць керавання стылю і інструментаў практычнасці рэкамендуюць вжываць === і !== у всіх случаях.
Чаму ў прыкладзе патрэбны any
Анотацыяў any не ёсць випадковымі. Якщо вы заявляеце зменныя з іх рэальнымі типамі, let num1: number = 10; і let num2: string = "10";, а потым пішаце num1 === num2, код не компілюецца. Кампайлер паведамляе пра адную з такіх памылак:
This comparison appears to be unintentional because the types 'number' and 'string' have no overlap.
TypeScript знае, што значэнне з типам number і значэнне з типам string ніколі не можа быць аднаковым, таму ён спрыводзіць такое порэвання як абэчку. Гэтыя правілы дапраўнаецца як да ==, так і да ===. Якшо обея зменныя задаць як any, гэты аналіз перакрыцча выключаецца, і гэта ўсё, што дазволяе кампілявацыю данага прыкладу. Це невялікі, але корыстны прыклад таго, як система типаў фіксуе беспраганастое порэвання прычынам, перш чым програма запускаецца, пры чым звычны JavaScript ніколі не паведамляе пра гэта. Це таксама паказвае, чаму any ёсць некарыстным: ён таямна выключае самэ гэты тип захавання. Па альтернатывамі дывіцеся шасць безпечных замены any.
Логічныя аператары
Логічныя аператары спаўнююць умовы. Іх ёсць трое: && (AND), || (OR) і ! (NOT). Пачніце з двух булевых значэнняў:
let b1 = true;
let b2 = false;
Потым спаўніце іх і адрыгніце ў працоўны стан:
console.log(b1 && b2); // false
console.log(b1 || b2); // true
console.log(!b2); // true
console.log(!b1); // false
Полная працэздатнась AND і OR для кожной пары вхідных дадзенняў:
trueіtrue:&&даеtrue,||таксама даеtrue.trueіfalse:&&даеfalse,||даеtrue.falseіtrue:&&даеfalse,||даеtrue.falseіfalse:&&даеfalse,||таксама даеfalse.
А для NOT:
!trueў значэнніfalse.!falseў значэнніtrue.
У словах:
&&(AND) ўжоўсяць строго. Ён вяртаеtrueтолькі калі оба значэння ўtrue; адна ціннасьfalseпрыводзіць да таго, што весь выраз будзеfalse.||(OR) ўжоўсяць менш строго. Дастатнека адна ціннасьtrue, і ён вяртаеfalseтолькі калі оба значэння ўfalse.!(NOT) прыймае адна значэнне і зміняе яго:trueстаеfalse, і навпакі.
Деталь, яка варта знання: ! завжды вяртае булева значэнне, але && і || на самай працо вяртаюць аднаго з своіх операндаў. Калі опэранды — булевыя значэння, результат таксама будзе булевым, як і ранейш. Калі ж опэранды — іншыя значэння, "" || "default" дае результат "default", а user && user.name дае аднаго з user чы ў user.name. Оба апэраторы таксама выконваюцься у скорачаны спосаб: якщо левая частка вже вярне рэшынку, правая частка ніколі не выконвуецца. Самэ гэтая якасць змушвае іх часта вжываць для задання значэння за замовчаннем і перакрыцця падазроўных ситуацый, а TypeScript прызначае результат паслядова.
Спалучэнне порэванняў з логікай
Апэраторы атрыбутаў і логікі зазвычай вжываюцца разам: порэванняя даюць булевыя значэння, а логічныя апэраторы спалучаюць іх.
console.log(20 > 10 && 20 < 10); // false
console.log(20 > 10 || 20 < 10); // true
У першым рэядзе 20 > 10 дае true, а 20 < 10 — false. Аператар AND выкарыстоўваецца тады, калі оба выразы ўжо true, таму рэзультат — false.
У другім рэядзе два выраўнання такія ж, але аператар OR патрэбуе толькі аднаго true, таму рэзультат — true. Паколькі аператары выраўнання працуюць строгае, чым && і ||, тут не патрэбны кранікі, хоця ўжыце ў іх можа спакойнаячыць чытанне слоžэных умов.
Падыгуванне і зніжэнне
++ дадае 1 да числа, а -- вынімае 1. Кожны з іх існуе у двух формах — прымусовай і пасляўней, і гэта разлік часта стварае плутанне. Пачніце з адной зменны:
let x: number = 10;
Калі аператар выкарыстоўваецца як самастоятельная запаведзь, обе формы даюць аднаковы рэзультат:
x++; // same as x = x + 1
console.log(x); // 11
++x; // same as x = x + 1
console.log(x); // 12
Якщо запісаць x++ і ++x на окремых рядках, які вони неявна дадуць адно да x. Гэтыя два варыянты паказваюць разныя рэзультаты толькі тады, калі значэнне выразу викорыстоўваецца ў той жа запіс, напрыклад, калі яго прызначаюць іншайяй зменной. Самэлькі тут і пачынае мяць значэння прыфіксны і постфіксны спосабы.
Постфіксны адбір: спачатку викорыстоўваецца значэнне, потым дадаецца адно
„Пост“ значыць пасля: выраз адмацаваецца як тэчнае значэнне зменной, а адбір выконваецца пасля гэтага.
let x: number = 10;
let res: number = x++;
Адпісванне обох зменных паказвае эфект:
console.log(res); // 10
console.log(x); // 11
Парядак падзеяў такі:
- Чытаецца тэчнае значэнне
x, тое есть10. - Гэтае значэнне стае рэзультатам выразу і зберагаецца ў
res. - Потым
xзбіраецца да11.
Таким чынам res застаеяць старую значэнне 10, а x прымае значэнне 11. Адыякцыя або ж дадзена; яна праішла толькі пасля копіювання значэння.
Адыякцыя з прыфіксам: аддаецца 1, пасля чаго выкарыстоўваецца значэнне
„Pre“ значыць „ранейш“: спачатку зменная адыякціюецца, а выраз дае значэнне пасля яго адыякцыі.
let x: number = 10;
let res: number = ++x;
Рэзультат падтверджае гэта:
console.log(res); // 11
console.log(x); // 11
Хутчэй порядак змініўся:
xпераходзіць з10на11.- Новае значэнне
11зберагаецца ўres.
Обе зменныя ў канцы застаюцца равнымі 11. Короткая версія: x++ дае значэнне до адыякцыі, ++x — значэнне пасля яе.
Дэадыякцыя таксама паводзіцца за тымі ж правіламі
-- являецца аналогам ++, але вырахоўвае разныя значэння шляхом вычитання заместо дадавання. Ось форма з прыфіксам:
let x: number = 10;
let res: number = --x;
А ўжо выхідны результат:
console.log(res); // 9
console.log(x); // 9
З прыфіксным зменшэнням x спачатку становіцца 9, і гэта нова значэнне падае ў res, таму оба выведаюць 9. Форма з пасыфіксам x-- будзе дзейсніць працоўна працупак: res запішае перваснае значэнне 10, а толькі пасля чаго x становіцца 9. Багатыя команды вообща утрымваюцца ад вжывання ++ і -- у большых выразах, аднойчы таму, што разлік прыфікса і пасыфікса лёгка сплутаць; x += 1 на самастоятнай лініі ніколі не ўскладняе розумэння.
Трынарны оператар умовы
Трынарны апэрадар выражае просты выбор if/else як адзін выраз. Ён выкарыстоўвае ? і :, і ён яэні апэрадар у JavaScript, які прымеў троха операндав, звядзей калі і падыходзіць гэта назва. Пачнім з двух цалых:
let a: number = 100;
let b: number = 200;
Потым выберіце большы з іх:
let res: number = (a > b) ? a : b;
console.log(res); // 200
Выраз складаецца з трох частак:
(a > b)прымкнуты да?— гэта умова.a, які знаходзится безпосередня пасля?, — гэта рысульт, калі умова ўтвараеtrue.b, які знаходзится пасля:, — гэта рысульт, калі умова ўтвараеfalse.
Чытаецца вголас: якщо a большэй за b, выкорыстоўваецца a, інакша — b. Паскольку 100 не большэй за 200, умова ўтварае false, і res становіцца 200.
Замена блока if/else
Галоўная перавага — кароткасць. Тая ж логіка, напісаная як запэўненне, займае калькі ліній і вымагае адзначэння res пры яго прызначэнні:
// Without ternary
let res: number;
if (a > b) {
res = a;
} else {
res = b;
}
Трынарная версія выканае тое ж у адной лініи. Гэтыя два фрагменты ўсьмо толькі альтернатывы, а не код для копіювання ў той самы контекст, адтолькі што дваразовае адзначэння res за дапамогою let будзе памылкай:
// With ternary — same result, one line
let res: number = (a > b) ? a : b;
Паколькі трынарная формула ўжо являе сабой выраз, яна таксама дазволяе адзначыць зменную і ініцыялізаваць яе ў адны крок, а TypeScript выважвае або пераканальваецца ў типе рэзультата з обох галак.
Спакоўванне значэння з абазавам
Троекацыйныя выразы таксама добра працуюць, калі галузі маюць іншы тип чым умова. У гэтым прыкладзе вік ператвараецца на описовую строчку:
let personage: number = 30;
let res: string = (personage > 18) ? "adult" : "minor";
console.log(res); // "adult"
30 > 18 ўзьме значэнне true, таму res будзе "adult". Калі вік становіць 15, умова будзе false, і res будзе "minor".
Вярніцеся да троекацыйных выразаў для простых, адзінаковых рашэнняў. Калі вы пачнёте ставіць адны троекацыйны выраз унутро іншага, каб пакрыць калькі варыянтав, чытаемасць раптам падупае, і ланцуг if...else if...else (או об’ект для пошуку) зазвычай є болей ясным.
Галоўныя выводы
- Аператары TypeScript — гэта аператары JavaScript; скомпільаваны выхід працуе абодва ідэнтычна пад час выканання.
/ ніколи не скрашчае значэнне, % вяртае залишок, а чыста большыя рэзультаты з ** трапляюцца без точнасці, якщо толькі не выкарыстоўваць bigint.+=, ўособлівае пазначэнне для «вырахаваць і зберагчы знову».=== і !==; слабая роўнасць ператварае типы способамі, якія можуць стварыць неспадзянанні.any тых захоўненняяў без звуку адменавае.&& і || працуюць па прынцыпу скорачання ліній і вяртаюць адны з операндаў, таму яны таксама служыць захоўнікамі і значэннямі-значальнікамі.x++ вяртае старое значэнне, прабіфікс ++x — новае; разліка прыкметна толькі тады, калі выкарыстоўваецца рэзультат.if...else.