javascript

ΠŸΠΎΡ‡Π΅ΠΌΡƒ стоит ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Tagged Unions ΠΏΡ€ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Π½Π° TypeScript

  • Π²Ρ‚ΠΎΡ€Π½ΠΈΠΊ, 5 августа 2025β€―Π³. Π² 00:00:02
https://habr.com/ru/companies/megafon/articles/933752/

πŸ‘‹ ΠŸΡ€ΠΈΠ²Π΅Ρ‚! МСня Π·ΠΎΠ²ΡƒΡ‚ АлСксандр, я Ρ€Π°Π±ΠΎΡ‚Π°ΡŽ Ρ„Ρ€ΠΎΠ½Ρ‚Π΅Π½Π΄-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠΌ Π² ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ «МСгаЀон». БСгодня я Ρ…ΠΎΡ‡Ρƒ ΠΏΠΎΠ³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ΡŒ Π½Π° Ρ‚Π΅ΠΌΡƒ Tagged Unions (Ρ€Π°Π·ΠΌΠ΅Ρ‡Π΅Π½Π½Ρ‹Ρ… объСдинСний) ΠΈ ΠΎΠ±ΡŠΡΡΠ½ΠΈΡ‚ΡŒ, ΠΏΠΎΡ‡Π΅ΠΌΡƒ ΠΎΠ½ΠΈ β€” ваш сСкрСтный инструмСнт для написания Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠ³ΠΎ TypeScript-ΠΊΠΎΠ΄Π°.

Π£Π»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΠ΅ Ρ‚ΠΈΠΏΠΈΠ·Π°Ρ†ΠΈΠΈ Π² TypeScript

Π’ Π΄ΠΈΠ½Π°ΠΌΠΈΡ‡Π½ΠΎ Ρ€Π°Π·Π²ΠΈΠ²Π°ΡŽΡ‰Π΅ΠΌΡΡ ΠΌΠΈΡ€Π΅ Π²Π΅Π±-Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ созданиС Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠ³ΠΎ, ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΠ΅ΠΌΠΎΠ³ΠΎ ΠΈ Π»Π΅Π³ΠΊΠΎ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΠΎΠ³ΠΎ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠ½ΠΎΠ³ΠΎ обСспСчСния являСтся ΠΊΠ»ΡŽΡ‡Π΅Π²ΠΎΠΉ Π·Π°Π΄Π°Ρ‡Π΅ΠΉ. TypeScript, Π±ΡƒΠ΄ΡƒΡ‡ΠΈ статичСски Ρ‚ΠΈΠΏΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΌ супСрсСтом JavaScript, Π±Ρ‹Π» Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π°Π½ ΠΈΠΌΠ΅Π½Π½ΠΎ для Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ этих Π²Ρ‹Π·ΠΎΠ²ΠΎΠ², особСнно Π² контСкстС Π±ΠΎΠ»ΡŒΡˆΠΈΡ… ΠΊΠΎΠ΄ΠΎΠ²Ρ‹Ρ… Π±Π°Π· ΠΈ ΠΊΠΎΠΌΠ°Π½Π΄Π½ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Ρ‹.

Π’Π²Π΅Π΄Π΅Π½ΠΈΠ΅ Ρ‚ΠΈΠΏΠΎΠ² позволяСт Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°ΠΌ Π²Ρ‹ΡΠ²Π»ΡΡ‚ΡŒ ошибки Π½Π° этапС компиляции, Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ сокращая количСство Π±Π°Π³ΠΎΠ², ΠΏΠΎΠΏΠ°Π΄Π°ΡŽΡ‰ΠΈΡ… Π² ΠΏΡ€ΠΎΠ΄Π°ΠΊΡˆΠ½, ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠ°Ρ ΠΏΡ€Π΅Π΄ΡΠΊΠ°Π·ΡƒΠ΅ΠΌΠΎΡΡ‚ΡŒ повСдСния ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ. Бтрогая типизация Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΠΎΠ²Ρ‹ΡˆΠ°Π΅Ρ‚ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π°, Π½ΠΎ ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠ°Π΅Ρ‚ Π΅Π³ΠΎ Ρ‡ΠΈΡ‚Π°Π΅ΠΌΠΎΡΡ‚ΡŒ, ΠΎΠ±Π»Π΅Π³Ρ‡Π°Π΅Ρ‚ процСссы Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³Π° ΠΈ способствуСт Π±ΠΎΠ»Π΅Π΅ эффСктивной ΠΊΠΎΠ»Π»Π°Π±ΠΎΡ€Π°Ρ†ΠΈΠΈ ΠΌΠ΅ΠΆΠ΄Ρƒ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°ΠΌΠΈ.

Однако, Π±Π΅Π· явного ΠΈ структурированного управлСния Ρ‚ΠΈΠΏΠ°ΠΌΠΈ Π΄Π°ΠΆΠ΅ Π² TypeScript ΠΌΠΎΠ³ΡƒΡ‚ Π²ΠΎΠ·Π½ΠΈΠΊΠ°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹. ИспользованиС ΡˆΠΈΡ€ΠΎΠΊΠΈΡ… Ρ‚ΠΈΠΏΠΎΠ², нСдискриминированных объСдинСний (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, string | number ΠΈΠ»ΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Ρ‹ с ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹ΠΌΠΈ свойствами, Ρ‚Π°ΠΊΠΈΠΌΠΈ ΠΊΠ°ΠΊ { propA?: string } | { propB?: number }), ΠΌΠΎΠΆΠ΅Ρ‚ привСсти ΠΊ ситуациям, ΠΊΠΎΠ³Π΄Π° компилятор Π½Π΅ способСн Ρ‚ΠΎΡ‡Π½ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ Ρ‚ΠΈΠΏ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΉ. Π­Ρ‚ΠΎ затрудняСт бСзопасный доступ ΠΊ спСцифичным свойствам, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ доступны Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‚Π΅, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎ ΠΏΡ€ΠΈΡΡƒΡ‚ΡΡ‚Π²ΡƒΡŽΡ‚ Π²ΠΎ всСх ΡΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‰ΠΈΡ… объСдинСния. Π’ Ρ‚Π°ΠΊΠΈΡ… сцСнариях Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°ΠΌ приходится ΠΏΠΎΠ»Π°Π³Π°Ρ‚ΡŒΡΡ Π½Π° Ρ€ΡƒΡ‡Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ наличия свойств (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, "property" in object) ΠΈΠ»ΠΈ ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€ typeof.

Π­Ρ‚ΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ Π½Π΅Π½Π°Π΄Π΅ΠΆΠ½Ρ‹ΠΌΠΈ, ΠΎΠ½ΠΈ Π½Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΡŽΡ‚ ΠΈΡΡ‡Π΅Ρ€ΠΏΡ‹Π²Π°ΡŽΡ‰Π΅ΠΉ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ всСх Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² ΠΈ, Ρ‡Ρ‚ΠΎ ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΎ, ΠΌΠΎΠ³ΡƒΡ‚ Π»Π΅Π³ΠΊΠΎ ΡΠ»ΠΎΠΌΠ°Ρ‚ΡŒΡΡ ΠΏΡ€ΠΈ Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³Π΅, ΠΏΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ измСнСния Π² ΠΎΠ΄Π½ΠΎΠΉ части ΠΊΠΎΠ΄Π° Π½Π΅ всСгда автоматичСски ΠΏΠΎΠ΄ΡΠ²Π΅Ρ‡ΠΈΠ²Π°ΡŽΡ‚ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ обновлСния Π² Π΄Ρ€ΡƒΠ³ΠΈΡ….

Рассмотрим ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π³Π΄Π΅ ΠΎΠ±Ρ‹Ρ‡Π½Ρ‹Π΅ объСдинСния Π½Π΅ Π΄Π°ΡŽΡ‚ достаточной ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ для бСзопасного доступа ΠΊ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ свойствам:

interface Car {
Β  move(): void;
Β  startEngine(): void;
}  

interface Bicycle {
Β  move(): void;
Β  gearCount: number;
}

declare function getVehicle(): Car | Bicycle;

let vehicle = getVehicle();

vehicle.move(); // OK, move() - ΠΎΠ±Ρ‰Π΅Π΅ свойство для ΠΎΠ±ΠΎΠΈΡ… Ρ‚ΠΈΠΏΠΎΠ²

vehicle.startEngine(); // Ошибка компиляции:
// Бвойство 'startEngine' отсутствуСт Π² Ρ‚ΠΈΠΏΠ΅ 'Car | Bicycle'. Β 

// TypeScript Π½Π΅ Π·Π½Π°Π΅Ρ‚, являСтся Π»ΠΈ 'vehicle' машиной ΠΈΠ»ΠΈ вСлосипСдом Π±Π΅Π· Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠΉ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ. Β 

if ("startEngine" in vehicle) { vehicle.startEngine(); }
// Π­Ρ‚ΠΎΡ‚ ΠΊΠΎΠ΄ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ, Π½ΠΎ ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅.
// К Ρ‚ΠΎΠΌΡƒ ΠΆΠ΅ это Π½Π΅ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ ΠΏΠΎΠ»Π½ΠΎΡ‚Ρ‹ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ всСх случаСв.

Π’ Ρ‚Π°ΠΊΠΈΡ… случаях TypeScript Π½Π΅ даст Π²Π°ΠΌ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ явно нСдопустимоС, Π½ΠΎ ΠΈ ΡƒΠ΄ΠΎΠ±Π½ΠΎΠ³ΠΎ ΠΈ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠ³ΠΎ способа Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с ΠΊΠ°ΠΆΠ΄Ρ‹ΠΌ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΌ Ρ‚ΠΈΠΏΠΎΠΌ Π²Π½ΡƒΡ‚Ρ€ΠΈ объСдинСния Ρƒ Π½Π΅Π³ΠΎ Π½Π΅Ρ‚.

ИмСнно здСсь Π½Π° сцСну выходят Tagged Unions (ΠΈΠ»ΠΈ дискриминированныС объСдинСния). Они ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡŽΡ‚ собой ΠΌΠΎΡ‰Π½Ρ‹ΠΉ ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Ρ€Π΅ΡˆΠ°Π΅Ρ‚ эти ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹, позволяя ΡΠΎΠ·Π΄Π°Π²Π°Ρ‚ΡŒ Ρ‚ΠΈΠΏΡ‹, ΠΏΡ€Π΅Π΄ΡΡ‚Π°Π²Π»ΡΡŽΡ‰ΠΈΠ΅ ΠΎΠ΄Π½Ρƒ ΠΈΠ· Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… Ρ„ΠΎΡ€ΠΌ, каТдая с ΠΎΡ‚Π»ΠΈΡ‡ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ свойством β€” "Ρ‚Π΅Π³ΠΎΠΌ" ΠΈΠ»ΠΈ "дискриминатором". Π­Ρ‚ΠΎ Π½Π΅ просто Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π΅Ρ‰Π΅ ΠΎΠ΄Π½ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° ΠΈΠ»ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… объСдинСний, это Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΉ сдвиг Π² ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π΅ ΠΊ ΠΌΠΎΠ΄Π΅Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Π΄Π°Π½Π½Ρ‹Ρ…. ΠŸΡ€ΠΎΡΡ‚Ρ‹Π΅ объСдинСния Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡƒΠΊΠ°Π·Ρ‹Π²Π°ΡŽΡ‚, Ρ‡Ρ‚ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Ρ‚ΠΈΠΏΠΎΠΌ А или Б.Β Tagged Union добавляСт ΡΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½ΡƒΡŽ ΠΌΠ΅Ρ‚ΠΊΡƒ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€,Β kind: 'A' ΠΈΠ»ΠΈ kind: 'B'), которая позволяСт TypeScript Ρ‚ΠΎΡ‡Π½ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΡ‚ΡŒ, с ΠΊΠ°ΠΊΠΈΠΌ ΠΈΠΌΠ΅Π½Π½ΠΎ Ρ‚ΠΈΠΏΠΎΠΌ ΠΎΠ½ сСйчас Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚.

Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ критичСски Π²Π°ΠΆΠ΅Π½ для создания слоТных систСм, Π³Π΄Π΅ Π΄Π°Π½Π½Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ мноТСство Ρ„ΠΎΡ€ΠΌ Π² зависимости ΠΎΡ‚ контСкста, ΠΈ Π³Π΄Π΅ трСбуСтся высокий ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ прСдсказуСмости ΠΈ надСТности. Π­Ρ‚ΠΎ заставляСт Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΎΠ² Ρ€Π°ΡΡΠΌΠ°Ρ‚Ρ€ΠΈΠ²Π°Ρ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ ΠΊΠ°ΠΊ Π½Π°Π±ΠΎΡ€ Ρ‡Π΅Ρ‚ΠΊΠΈΡ…, Π²Π·Π°ΠΈΠΌΠΎΠΈΡΠΊΠ»ΡŽΡ‡Π°ΡŽΡ‰ΠΈΡ… состояний. Π’Π°ΠΊΠΎΠΉ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ Π½Π΅ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡƒΠ»ΡƒΡ‡ΡˆΠ°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ с Ρ‚ΠΈΠΏΠ°ΠΌΠΈ, Π½ΠΎ ΠΈ Π²Π΅Π΄Π΅Ρ‚ ΠΊ созданию Π±ΠΎΠ»Π΅Π΅ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΠ³ΠΎ ΠΊΠΎΠ΄Π°.

Π§Ρ‚ΠΎ Ρ‚Π°ΠΊΠΎΠ΅ Tagged Unions?

Tagged UnionΒ (Ρ‚Π°ΠΊΠΆΠ΅ извСстный как дискриминантноС объСдинСниС, sum type, variant record,Β choice typeΒ ΠΈΠ»ΠΈΒ disjoint union) β€” это Ρ‚ΠΈΠΏ объСдинСния, Π³Π΄Π΅ ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ содСрТит ΠΎΠ±Ρ‰Π΅Π΅ свойство-дискриминатор Π»ΠΈΡ‚Π΅Ρ€Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ°. Π­Ρ‚ΠΎ свойство позволяСт TypeScript Ρ‚ΠΎΡ‡Π½ΠΎ ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ ΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°Ρ…, гарантируя бСзопасный доступ ΠΊ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ свойствам и обСспСчивая ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ всСх Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… случаСв.

Рассмотрим классичСский ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Tagged Union для гСомСтричСских Ρ„ΠΈΠ³ΡƒΡ€:

interface Circle {  
    kind: 'circle'; // Дискриминатор: свойство 'kind' с Π»ΠΈΡ‚Π΅Ρ€Π°Π»ΡŒΠ½Ρ‹ΠΌ Ρ‚ΠΈΠΏΠΎΠΌ 'circle'  
Β  Β  radius: number;  
}  
  
interface Rectangle {  
Β  Β  kind: 'rectangle'; // Дискриминатор: свойство 'kind' с Π»ΠΈΡ‚Π΅Ρ€Π°Π»ΡŒΠ½Ρ‹ΠΌ Ρ‚ΠΈΠΏΠΎΠΌ 'rectangle'  
Β  Β  width: number;  
Β  Β  height: number;  
}  
  
type Shape = Circle | Rectangle; // ΠžΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΡŽΡ‰ΠΈΠΉ Ρ‚ΠΈΠΏ, состоящий ΠΈΠ· Circle ΠΈ Rectangle  

Π’ этом ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ kind являСтся свойством-дискриминатором. Π•Π³ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ('circle' ΠΈΠ»ΠΈ 'rectangle') ΠΎΠ΄Π½ΠΎΠ·Π½Π°Ρ‡Π½ΠΎ ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Π½Π° ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΡƒΡŽ Ρ„ΠΎΡ€ΠΌΡƒ, позволяя TypeScript Ρ€Π°Π·Π»ΠΈΡ‡Π°Ρ‚ΡŒ Ρ‡Π»Π΅Π½Ρ‹ объСдинСния.

ΠœΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ Tagged Unions основан на автоматичСском суТСнии Ρ‚ΠΈΠΏΠΎΠ² Ρ‡Π΅Ρ€Π΅Π· ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»ΡŒΠ½Ρ‹Π΅ Ρ‚ΠΎΡ‡ΠΊΠΈ (type guards). Π’ Π±Π»ΠΎΠΊΠ°Ρ… условной Π»ΠΎΠ³ΠΈΠΊΠΈ (switch,Β if/else) TypeScriptΒ Π°Π½Π°Π»ΠΈΠ·ΠΈΡ€ΡƒΠ΅Ρ‚ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ дискриминатора и опрСдСляСт Ρ‚ΠΎΡ‡Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°. Π­Ρ‚ΠΎ позволяСт компилятору статичСски Π²Π΅Ρ€ΠΈΡ„ΠΈΡ†ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ доступ к ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ свойствам ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°, ΠΏΠΎΠ»Π½ΠΎΡΡ‚ΡŒΡŽ ΠΈΡΠΊΠ»ΡŽΡ‡Π°Ρ ошибки обращСния ΠΊ Π½Π΅ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΌ полям.

Рассмотрим Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ, которая вычисляСт ΠΏΠ»ΠΎΡ‰Π°Π΄ΡŒ Ρ„ΠΈΠ³ΡƒΡ€Ρ‹:

function getArea(shape: Shape): number {  
Β  Β  switch (shape.kind) {  
Β  Β  Β  Β  case 'circle':  
Β  Β  Β  Β  Β  Β  // Π—Π΄Π΅ΡΡŒ TypeScript Ρ‚ΠΎΡ‡Π½ΠΎ Π·Π½Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ 'shape' являСтся Ρ‚ΠΈΠΏΠΎΠΌ Circle,  
Β  Β  Β  Β  Β  Β  // ΠΈ автоматичСски ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅Ρ‚ доступ ΠΊ свойству 'radius'.  
Β  Β  Β  Β  Β  Β  return Math.PI * shape.radius ** 2;  
Β  Β  Β  Β  case 'rectangle':  
Β  Β  Β  Β  Β  Β  // Аналогично, здСсь 'shape' суТаСтся Π΄ΠΎ Ρ‚ΠΈΠΏΠ° Rectangle,  
Β  Β  Β  Β  Β  Β  // прСдоставляя бСзопасный доступ ΠΊ 'width' ΠΈ 'height'.  
Β  Β  Β  Β  Β  Β  return shape.width * shape.height;  
Β  Β  }  
}  

Π­Ρ‚ΠΎΡ‚ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ устраняСт Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡΡ‚ΡŒ Π² Ρ€ΡƒΡ‡Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°Ρ… typeof ΠΈΠ»ΠΈ in ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π°Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΌΠ΅Π½Π΅Π΅ Π½Π°Π΄Π΅ΠΆΠ½Ρ‹ΠΌΠΈ ΠΈ Π±ΠΎΠ»Π΅Π΅ многословными.

Дискриминатор ΠΏΡ€Π΅ΠΎΠ±Ρ€Π°Π·ΡƒΠ΅Ρ‚ Π½Π΅ΠΎΠ΄Π½ΠΎΠ·Π½Π°Ρ‡Π½ΠΎΠ΅ объСдинСниС Π² Ρ‡Ρ‘Ρ‚ΠΊΠΎ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΠΌΡ‹Π΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹. Π‘Π΅Π· Π½Π΅Π³ΠΎ TypeScript Π·Π½Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ, Ρ‡Ρ‚ΠΎ пСрСмСнная относится ΠΊΒ A | B, вынуТдая Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ° ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π½Π΅Π½Π°Π΄Ρ‘ΠΆΠ½Ρ‹Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ для доступа ΠΊ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ свойствам. Tagged Unions Ρ€Π΅ΡˆΠ°ΡŽΡ‚ это Ρ‡Π΅Ρ€Π΅Π· ΠΎΠ±Ρ‰Π΅Π΅ свойство с ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ Π»ΠΈΡ‚Π΅Ρ€Π°Π»ΠΎΠΌ для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°. ΠŸΡ€ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ΅Β if (obj.kind === 'circle')Β ΠΈΠ»ΠΈΒ case 'circle'Β TypeScriptΒ Ρ‚ΠΎΡ‡Π½ΠΎ опрСдСляСт конкрСтный Ρ‚ΠΈΠΏ Π²Π½ΡƒΡ‚Ρ€ΠΈ Π±Π»ΠΎΠΊΠ°, Π° Π½Π΅ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»Π°Π³Π°Π΅Ρ‚ Π΅Π³ΠΎ. Π­Ρ‚ΠΎ создаёт явный ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ с систСмой Ρ‚ΠΈΠΏΠΎΠ², обСспСчивая:

  • ΠΈΡΡ‡Π΅Ρ€ΠΏΡ‹Π²Π°ΡŽΡ‰ΡƒΡŽ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ всСх Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ²

  • Π°Π²Ρ‚ΠΎΠ΄ΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π² IDE

  • ΠΏΡ€Π΅Π΄ΡΠΊΠ°Π·ΡƒΠ΅ΠΌΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° для компилятора ΠΈ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°.

ΠŸΠΎΡ‡Π΅ΠΌΡƒ Tagged Unions β€” это ΠΌΠΎΡ‰Π½Ρ‹ΠΉ ΠΏΠ°Ρ‚Ρ‚Π΅Ρ€Π½ проСктирования?

ΠŸΠΎΠ²Ρ‹ΡˆΠ΅Π½Π½Π°Ρ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ Π½Π° этапС компиляции

Tagged Unions Π²Ρ‹ΡΠ²Π»ΡΡŽΡ‚ слоТныС логичСскиС ошибки Π½Π° этапС компиляции, Π° Π½Π΅ Π²ΠΎ врСмя выполнСния. Π­Ρ‚ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΡƒΠ΅Ρ‚ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏ "Shift-Left": ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΡ‹, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π²(?) TypeScript ΠΏΡ€ΠΎΡΠ²ΠΈΠ»ΠΈΡΡŒ Π±Ρ‹ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΏΡ€ΠΈ тСстировании ΠΈΠ»ΠΈ Π² ΠΏΡ€ΠΎΠ΄Π°ΠΊΡˆΠ΅Π½Π΅, ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ²Π°ΡŽΡ‚ΡΡ сразу ΠΏΡ€ΠΈ написании ΠΊΠΎΠ΄Π°. Π­Ρ‚ΠΎ обСспСчиваСт:

  1. Π Π°Π½Π½Π΅Π΅ ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠ΅Π½ΠΈΠ΅ Π±Π°Π³ΠΎΠ²Β - ошибки Ρ„ΠΈΠΊΡΠΈΡ€ΡƒΡŽΡ‚ΡΡ Π΄ΠΎ тСстирования

  2. Π“Π°Ρ€Π°Π½Ρ‚ΠΈΡŽ ΠΏΠΎΠ»Π½ΠΎΡ‚Ρ‹Β ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ - компилятор провСряСт ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ всСх Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² Π΄Π°Π½Π½Ρ‹Ρ…

  3. Π‘Π½ΠΈΠΆΠ΅Π½ΠΈΠ΅ стоимости - исправлСниС Π½Π° этапС Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π² 10-100 Ρ€Π°Π· дСшСвлС

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ ΠΏΠΎΠ²Ρ‹ΡˆΠ°Π΅Ρ‚ΡΡ Π½Π°Π΄Π΅ΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° ΠΈ ΡΠ½ΠΈΠΆΠ°ΡŽΡ‚ΡΡ риски Π² слоТных ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ….

Π˜ΡΡ‡Π΅Ρ€ΠΏΡ‹Π²Π°ΡŽΡ‰Π°Ρ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° (Exhaustiveness сhecking)

TypeScript ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅Π΄ΠΈΡ‚ΡŒ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, Ссли ΠΎΠ½ Π·Π°Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΉ случай Π² switch ΠΎΠΏΠ΅Ρ€Π°Ρ‚ΠΎΡ€Π΅, гарантируя, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ΄ ΡƒΡ‡ΠΈΡ‚Ρ‹Π²Π°Π΅Ρ‚ всС сцСнарии. Π’ΠΎΡ‚ ΠΊΠ°ΠΊ это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ΅:

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number }
  | { kind: "rectangle"; width: number; height: number };

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle": 
      return Math.PI * shape.radius ** 2;
    case "square": 
      return shape.side ** 2;
    case "rectangle": 
      return shape.width * shape.height;
    default:
      // Exhaustiveness сhecking
      // Если TypeScript ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΡ‚ Π½Π΅ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚, 
      // shape здСсь Π±ΡƒΠ΄Π΅Ρ‚ ΠΈΠΌΠ΅Ρ‚ΡŒ Ρ‚ΠΈΠΏ, ΠΎΡ‚Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΎΡ‚ never
      const _exhaustiveCheck: never = shape;
      throw new Error(`Unhandled shape: ${_exhaustiveCheck}`);
  }
}

ΠŸΡ€ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° Π² объСдинСниС Typescript выдаст ΠΎΡˆΠΈΠ±ΠΊΡƒ:

type Shape = 
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number }
  | { kind: "rectangle"; width: number; height: number }
  | { kind: "triangle"; base: number; height: number }; // Новый Ρ‚ΠΈΠΏ!

ERROR: Type 'triangle' is not assignable to type 'never'.

Ошибка ΡƒΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ Π²Β switch отсутствуСт ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°Β triangle. ПослС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π½ΠΎΠ²ΠΎΠ³ΠΎ Ρ‚ΠΈΠΏΠ° Π² switchΒ shapeΒ Π²Β default снова станСт Ρ‚ΠΈΠΏΠΎΠΌΒ never ΠΈ ошибка исчСзнСт.

Π£Π»ΡƒΡ‡ΡˆΠ΅Π½Π½ΠΎΠ΅ суТСниС Ρ‚ΠΈΠΏΠΎΠ² (Type narrowing)

Tagged Unions ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ TypeScript автоматичСски ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΡΡ‚ΡŒ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ Π²Π½ΡƒΡ‚Ρ€ΠΈ условных Π±Π»ΠΎΠΊΠΎΠ² благодаря свойству-дискриминатору. Π­Ρ‚ΠΎ замСняСт рискованныС Ρ€ΡƒΡ‡Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ (as Type) ΠΈ Π΄Π΅Π»Π°Π΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Ρƒ со слоТными Π΄Π°Π½Π½Ρ‹ΠΌΠΈ бСзопасной.

Π’ IDE это Π΄Π°Ρ‘Ρ‚ Ρ‚ΠΎΡ‡Π½ΠΎΠ΅ Π°Π²Ρ‚ΠΎΠ΄ΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅. Π‘Π΅Π· Tagged Unions для объСдинСний (Dog | Cat) Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€ ΠΏΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΎΠ±Ρ‰ΠΈΠ΅ свойства. Π Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΡƒ приходится Π²Ρ€ΡƒΡ‡Π½ΡƒΡŽ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΡΡ‚ΡŒ Ρ‚ΠΈΠΏΡ‹, Ρ‡Ρ‚ΠΎ замСдляСт Ρ€Π°Π±ΠΎΡ‚Ρƒ. Π‘ Tagged Unions послС ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ дискриминатора TypeScript ΠΌΠ³Π½ΠΎΠ²Π΅Π½Π½ΠΎ распознаёт ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ, ΠΈ IDE ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ€Π΅Π»Π΅Π²Π°Π½Ρ‚Π½Ρ‹Π΅ свойства.

БистСма Π±Π΅Ρ€Ρ‘Ρ‚ Π½Π° сСбя Π·Π°ΠΏΠΎΠΌΠΈΠ½Π°Π½ΠΈΠ΅ структур Π΄Π°Π½Π½Ρ‹Ρ…, сниТая ΠΊΠΎΠ³Π½ΠΈΡ‚ΠΈΠ²Π½ΡƒΡŽ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ. Π­Ρ‚ΠΎ ускоряСт написаниС ΠΊΠΎΠ΄Π°, ΡƒΠΌΠ΅Π½ΡŒΡˆΠ°Π΅Ρ‚ ошибки ΠΈ позволяСт ΡΠΎΡΡ€Π΅Π΄ΠΎΡ‚ΠΎΡ‡ΠΈΡ‚ΡŒΡΡ Π½Π° Π»ΠΎΠ³ΠΈΠΊΠ΅, Π° Π½Π΅ Ρ‚ΠΈΠΏΠ°Ρ….

Чистый ΠΊΠΎΠ΄ ΠΈ ΡƒΡΡ‚ΠΎΠΉΡ‡ΠΈΠ²ΠΎΡΡ‚ΡŒ ΠΊ Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³Ρƒ

Tagged Unions ΠΎΠ±Π΅ΡΠΏΠ΅Ρ‡ΠΈΠ²Π°ΡŽΡ‚ чистоту ΠΊΠΎΠ΄Π° ΠΈ бСзопасный Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³ Ρ‡Π΅Ρ€Π΅Π· явноС описаниС всСх состояний. Явная структура ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° создаёт ΡΠ°ΠΌΠΎΠ΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ€ΡƒΡŽΡ‰ΡƒΡŽΡΡ систСму Ρ‚ΠΈΠΏΠΎΠ², Π³Π΄Π΅ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ сразу видят допустимыС состояния ΠΈ ΠΈΡ… Π΄Π°Π½Π½Ρ‹Π΅. Π­Ρ‚ΠΎ сниТаСт ΠΊΠΎΠ³Π½ΠΈΡ‚ΠΈΠ²Π½ΡƒΡŽ Π½Π°Π³Ρ€ΡƒΠ·ΠΊΡƒ ΠΏΡ€ΠΈ Ρ‡Ρ‚Π΅Π½ΠΈΠΈ ΠΊΠΎΠ΄Π° ΠΈ ускоряСт ΠΏΠΎΠ½ΠΈΠΌΠ°Π½ΠΈΠ΅ Π»ΠΎΠ³ΠΈΠΊΠΈ Π΄Π°ΠΆΠ΅ спустя мСсяцы.

ΠŸΡ€ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΈ состояний TypeScript автоматичСски Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ Π½Π΅ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Π½Ρ‹Π΅ случаи благодаря ΠΈΡΡ‡Π΅Ρ€ΠΏΡ‹Π²Π°ΡŽΡ‰Π΅ΠΉ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ΅. Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° ΠΈΠ»ΠΈ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ Π½Π΅ΠΌΠ΅Π΄Π»Π΅Π½Π½ΠΎ подсвСчиваСт мСста для обновлСния, прСдотвращая ошибки Π΄ΠΎ дСплоя. БистСма Ρ‚ΠΈΠΏΠΎΠ² становится Π°ΠΊΡ‚ΠΈΠ²Π½Ρ‹ΠΌ соавтором: вмСсто Ρ€ΡƒΡ‡Π½ΠΎΠ³ΠΎ поиска зависимостСй компилятор Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³ (ΠΏΠ΅Ρ€Π΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΠ΅, ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ сигнатур) Π½Π΅ сломаСт ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ состояний.

Π’Π°ΠΊΠΈΠΌ ΠΎΠ±Ρ€Π°Π·ΠΎΠΌ, ΠΊΠΎΠ΄ устойчив ΠΊ измСнСниям Π΄Π°ΠΆΠ΅ Π² Π±ΠΎΠ»ΡŒΡˆΠΈΡ… ΠΈ слоТных ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ…, Π° Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³ прСвращаСтся ΠΈΠ· рискованной ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ Π² прСдсказуСмый процСсс.

Π₯ΠΎΡ€ΠΎΡˆΠ°Ρ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠ° IDE

Π‘ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ IDE, Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠ°ΠΊ Visual Studio Code, ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚ Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½Π½ΠΎΠ΅ Π°Π²Ρ‚ΠΎΠ΄ΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΈ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΡƒ ошибок ΠΏΡ€ΠΈ Ρ€Π°Π±ΠΎΡ‚Π΅ с Tagged Unions. IDE ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ ΠΎ дискриминаторах для Ρ‚ΠΎΡ‡Π½Ρ‹Ρ… подсказок свойств ΠΈ бСзопасного Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³Π°. Π­Ρ‚ΠΎ сокращаСт количСство ΠΎΠΏΠ΅Ρ‡Π°Ρ‚ΠΎΠΊ ΠΈ ускоряСт Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ Π·Π° счёт ΠΈΠ½Ρ‚Π΅Π³Ρ€ΠΈΡ€ΠΎΠ²Π°Π½Π½ΠΎΠΉ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ Ρ‚ΠΈΠΏΠΎΠ² прямо Π² Ρ€Π΅Π΄Π°ΠΊΡ‚ΠΎΡ€Π΅.

ΠŸΡ€Π°ΠΊΡ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠ΅ cΡ†Π΅Π½Π°Ρ€ΠΈΠΈ использования Tagged Unions

Π£ΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ cостояниСм (State Management)

Tagged Unions ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚ ΠΌΠΎΡ‰Π½Ρ‹ΠΉ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ для строгого модСлирования состояний Π² прилоТСниях, особСнно ΠΊΠΎΠ³Π΄Π° Ρ€Π΅Ρ‡ΡŒ ΠΈΠ΄Ρ‘Ρ‚ ΠΎΠ± асинхронных опСрациях ΠΈΠ»ΠΈ слоТных UI-ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π°Ρ…. Π˜Ρ… Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ΅ прСимущСство Π·Π°ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Π²Β ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΈ логичСски Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΉ состояний на ΡƒΡ€ΠΎΠ²Π½Π΅ систСмы Ρ‚ΠΈΠΏΠΎΠ², Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΠΈΠ°Π»ΡŒΠ½ΠΎ ΠΎΡ‚Π»ΠΈΡ‡Π°Π΅Ρ‚ этот ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ ΠΎΡ‚ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ² управлСния состояниСм.

Рассмотрим Ρ‚ΠΈΠΏΠΈΡ‡Π½Ρ‹ΠΉ сцСнарий Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠΈ Π΄Π°Π½Π½Ρ‹Ρ…:

type LoadingState = { status: 'loading' };
type LoadedState<T> = { status: 'loaded'; data: T };
type ErrorState = { status: 'error'; message: string; errorCode?: number };

type UIState<T> = LoadingState | LoadedState<T> | ErrorState;

ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ объСдинСния содСрТит явный ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ состояния status, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ строго опрСдСляСт связанныС Π΄Π°Π½Π½Ρ‹Π΅Β Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для Ρ€Π΅Π»Π΅Π²Π°Π½Ρ‚Π½Ρ‹Ρ… состояний.

Π’ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΈΠ½Π³Π° TypeScript ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ дискриминатор status для Ρ‚ΠΎΡ‡Π½ΠΎΠ³ΠΎ суТСния Ρ‚ΠΈΠΏΠ°:

function renderUI<T>(state: UIState<T>): React.ReactNode {
  switch (state.status) {
    case 'loading':
      return <Spinner />;

    case 'loaded':
      // TypeScript Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚ доступ ΠΊ state.data
      return <DataView content={state.data} />;
      
    case 'error':
      // Доступны Π’ΠžΠ›Π¬ΠšΠž свойства ErrorState
      return <ErrorMessage 
               message={state.message} 
               code={state.errorCode} 
             />;
             
    default:
      // Π—Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ Π±ΡƒΠ΄ΡƒΡ‰ΠΈΡ… ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠΉ
      const _exhaustiveCheck: never = state;
      throw new Error(`Unhandled status: ${_exhaustiveCheck}`);
  }
}

ΠšΠ»ΡŽΡ‡Π΅Π²Ρ‹Π΅ прСимущСства ΠΏΠ΅Ρ€Π΅Π΄ Ρ‚Ρ€Π°Π΄ΠΈΡ†ΠΈΠΎΠ½Π½Ρ‹ΠΌΠΈ ΠΏΠΎΠ΄Ρ…ΠΎΠ΄Π°ΠΌΠΈ:

  • УстранСниС ΠΏΡ€ΠΎΡ‚ΠΈΠ²ΠΎΡ€Π΅Ρ‡ΠΈΠ²Ρ‹Ρ… состояний. Π’ классовых Ρ€Π΅ΡˆΠ΅Π½ΠΈΡΡ… часто Π²ΡΡ‚Ρ€Π΅Ρ‡Π°ΡŽΡ‚ΡΡ Ρ„Π»Π°Π³ΠΈ Π²Ρ€ΠΎΠ΄Π΅Β isLoading,Β isError,Β data, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π½Π°Ρ…ΠΎΠ΄ΠΈΡ‚ΡŒΡΡ Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²ΠΎΡ€Π΅Ρ‡ΠΈΠ²Ρ‹Ρ… комбинациях (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€,Β isLoading=trueΒ ΠΈΒ data=[...]). Tagged Unions Π΄Π΅Π»Π°ΡŽΡ‚ Ρ‚Π°ΠΊΠΈΠ΅ ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΠΈΒ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ΠΌΠΈ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ Ρ‚ΠΈΠΏΠΎΠ²

  • АвтоматичСская валидация Π΄Π°Π½Π½Ρ‹Ρ…. ΠŸΡ€ΠΈ статусС 'loading' компилятор Π±Π»ΠΎΠΊΠΈΡ€ΡƒΠ΅Ρ‚ ΠΏΠΎΠΏΡ‹Ρ‚ΠΊΠΈ доступа ΠΊΒ state.dataΒ ΠΈΠ»ΠΈΒ state.message, Ρ‡Ρ‚ΠΎ обнаруТиваСтся сразу ΠΏΡ€ΠΈ написании ΠΊΠΎΠ΄Π°, Π° Π½Π΅ Π² Ρ€Π°Π½Ρ‚Π°ΠΉΠΌΠ΅.

  • Бинхронизация UI с бизнСс-Π»ΠΎΠ³ΠΈΠΊΠΎΠΉ. Π”ΠΎΠ±Π°Π²Π»Π΅Π½ΠΈΠ΅ Π½ΠΎΠ²ΠΎΠ³ΠΎ состояния (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€,Β 'initial') Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€ΡƒΠ΅Ρ‚ ошибки компиляции Π²ΠΎ всСх мСстах ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ, гарантируя ΡΠΎΠ³Π»Π°ΡΠΎΠ²Π°Π½Π½ΠΎΡΡ‚ΡŒ Π»ΠΎΠ³ΠΈΠΊΠΈ ΠΈ интСрфСйса.

ΠžΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² API

Tagged Unions ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‚ типобСзопасно ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Ρ‚ΡŒ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚Ρ‹ ΠΎΡ‚Π²Π΅Ρ‚ΠΎΠ² ΠΎΡ‚ сСрвСра, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΡƒΡΠΏΠ΅ΡˆΠ½Ρ‹ΠΉ ΠΎΡ‚Π²Π΅Ρ‚ с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ ΠΈΠ»ΠΈ ΠΎΡ‚Π²Π΅Ρ‚ с ошибкой. Π­Ρ‚ΠΎ Π³Π°Ρ€Π°Π½Ρ‚ΠΈΡ€ΡƒΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ всС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ ΠΎΡ‚Π²Π΅Ρ‚Ρ‹ ΠΎΡ‚ сСрвСра (успСх, ошибка) Π±ΡƒΠ΄ΡƒΡ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Π½Ρ‹ ΠΏΡ€Π°Π²ΠΈΠ»ΡŒΠ½ΠΎ.

type ApiSuccess<T> = {  
Β  Β  response_type: 'success';  
Β  Β  data: T;  
};  
  
type ApiError = {  
Β  Β  response_type: 'error';  
Β  Β  message: string;  
Β  Β  statusCode: number;  
};  
  
type ApiResponse<T> = ApiSuccess<T> | ApiError;  
  
async function fetchData<T>(url: string): Promise<ApiResponse<T>> {  
Β  Β  try {  
Β  Β  Β  Β  const response = await fetch(url);  
Β  Β  Β  Β  if (!response.ok) {  
Β  Β  Β  Β  Β  Β  return {  
Β  Β  Β  Β  Β  Β  Β  Β  response_type: 'error',  
                message: `Ошибка HTTP: ${response.statusText}`,  
Β  Β  Β  Β  Β  Β  Β  Β  statusCode: response.status  
Β  Β  Β  Β  Β  Β  };  
Β  Β  Β  Β  }  
Β  Β  Β  Β  const data: T = await response.json();  
Β  Β  Β  Β  return { response_type: 'success', data };  
Β  Β  } catch (error: any) {  
Β  Β  Β  Β  return {  
Β  Β  Β  Β  Β  Β  response_type: 'error',  
Β  Β  Β  Β  Β  Β  message: error instanceof Error? error.message : 'НСизвСстная ошибка',  
Β  Β  Β  Β  Β  Β  statusCode: 500  
Β  Β  Β  Β  };  
Β  Β  }  
}  


// ИспользованиС:  

type UserData = { id: number; name: string; };

async function processUserResponse() {  Β  Β    
Β  Β  const result = await fetchData<UserData>('/api/users/1');  
  
Β  Β  if (result.response_type === 'success') {  
Β  Β  Β  Β  console.log(`ΠŸΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒ: ${result.data.name}`); // TypeScript Π·Π½Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ result.data сущСствуСт  
Β  Β  } else {  
Β  Β  Β  Β  console.error(`Ошибка ΠΏΡ€ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½ΠΈΠΈ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ: ${result.message} (Код: ${result.statusCode})`); // TypeScript Π·Π½Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ result.message ΠΈ statusCode ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‚  
Β  Β  }  
} 

Π’Π°ΠΊΠΎΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΈΡΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ ситуации, ΠΊΠΎΠ³Π΄Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊ Π·Π°Π±Ρ‹Π²Π°Π΅Ρ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ ΠΊΠΎΠ΄ ошибки ΠΈΠ»ΠΈ пытаСтся ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒΒ dataΒ ΠΏΡ€ΠΈ ошибкС API.

РСализация Option / Result Ρ‚ΠΈΠΏΠΎΠ²

Tagged Unions идСально Π²ΠΎΠΏΠ»ΠΎΡ‰Π°ΡŽΡ‚ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Π΅ ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ, Π³Π΄Π΅ отсутствиС значСния β€” явноС состояниС систСмы.

Рассмотрим Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Ρ‚ΠΈΠΏΠ°Β OptionΒ (Π°Π½Π°Π»ΠΎΠ³Β MaybeΒ Π² Haskell) β€” Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ инструмСнта для ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ nullable-Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΉ Π±Π΅Π· риска ошибок с Β undefined/null:

type None = { kind: "none" };
type Some<T> = { kind: "some"; value: T };
type Option<T> = None | Some<T>;

Π’ΠΈΠΏ NoneΒ β€” явноС прСдставлСниС "отсутствия значСния" (Π±Π΅Π·Β null!). Π’ΠΈΠΏ Some<T> β€” ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ для ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ значСния Ρ‚ΠΈΠΏΠ°Β T.

ΠŸΡ€ΠΈΠΌΠ΅Ρ€:

const getUserName = (user: Option<User>): string => {
  switch (user.kind) {
    case "none":
      return "Π“ΠΎΡΡ‚ΡŒ"; // Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Π·Π°Π³Π»ΡƒΡˆΠΊΡƒ ΠΊΠΎΠ³Π΄Π° Π½Π΅Ρ‚ ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ
    case "some":
      return user.value.name; // TypeScript Π·Π½Π°Π΅Ρ‚, Ρ‡Ρ‚ΠΎ user.value сущСствуСт
  }
};

// ИспользованиС:
const loggedInUser: Some<User> = {
    kind: "some",
    value: {
      name: "Иван"
    }
};
const guestUser: None = { kind: "none" };

getUserName(loggedInUser); // "Иван"
getUserName(guestUser);    // "Π“ΠΎΡΡ‚ΡŒ"

Π’Π°ΠΊ ΠΆΠ΅ рассмотрим Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Ρ‚ΠΈΠΏΠ° Result. Он прСдставляСт Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ Π»ΠΈΠ±ΠΎ успСхом (Success<T>) с Π΄Π°Π½Π½Ρ‹ΠΌΠΈ, Π»ΠΈΠ±ΠΎ ошибкой (Failure<E>) с ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠ΅ΠΉ ΠΎΠ± ошибкС. Π’ ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠ΅ ΠΎΡ‚ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ,Β ResultΒ Π΄Π΅Π»Π°Π΅Ρ‚ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΡƒ ошибок явной ΠΈ типобСзопасной.

type Success<T> = { status: 'success'; data: T; };  
type Failure<E> = { status: 'failure'; error: E; }; 
 
type Result<T, E> = Success<T> | Failure<E>; 

// ИспользованиС:
function parseNumber(input: string): Result<number, string> {  
Β  Β  const num = parseInt(input);  
Β  Β  if (isNaN(num)) {  
Β  Β  Β  Β  return { status: 'failure', error: `НСвСрный Π²Π²ΠΎΠ΄: "${input}" Π½Π΅ являСтся числом.` };  
Β  Β  }  
Β  Β  return { status: 'success', data: num };  
}  
  
const parsed = parseNumber('123');  

if (parsed.status === 'success') {  
Β  Β  console.log(`ΠŸΠ°Ρ€ΡΠΈΠ½Π³ ΡƒΡΠΏΠ΅ΡˆΠ΅Π½: ${parsed.data}`);  
} else {  
Β  Β  console.error(`Ошибка парсинга: ${parsed.error}`);  
}

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

ДискриминируСмыС объСдинСния Π² TypeScript ΡΠ²Π»ΡΡŽΡ‚ΡΡ Ρ„ΡƒΠ½Π΄Π°ΠΌΠ΅Π½Ρ‚Π°Π»ΡŒΠ½Ρ‹ΠΌ инструмСнтом для Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π½Π°Π΄Π΅ΠΆΠ½Ρ‹Ρ…, ΠΌΠ°ΡΡˆΡ‚Π°Π±ΠΈΡ€ΡƒΠ΅ΠΌΡ‹Ρ… ΠΈ бСзопасных ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΉ. Они ΠΏΡ€Π΅Π΄ΠΎΡΡ‚Π°Π²Π»ΡΡŽΡ‚ ΠΌΠΎΡ‰Π½Ρ‹ΠΉ ΠΌΠ΅Ρ…Π°Π½ΠΈΠ·ΠΌ для модСлирования слоТных структур Π΄Π°Π½Π½Ρ‹Ρ…, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Ρ‚ΡŒ Ρ€Π°Π·Π»ΠΈΡ‡Π½Ρ‹Π΅, Π½ΠΎ ΠΏΡ€ΠΈ этом фиксированныС Ρ„ΠΎΡ€ΠΌΡ‹, обСспСчивая Π²Ρ‹ΡΠΎΠΊΡƒΡŽ ΡΡ‚Π΅ΠΏΠ΅Π½ΡŒ типобСзопасности ΠΈ прСдсказуСмости ΠΊΠΎΠ΄Π°.

Как Π±Ρ‹Π»ΠΎ ΠΏΠΎΠΊΠ°Π·Π°Π½ΠΎ, дискриминируСмыС объСдинСния Π·Π½Π°Ρ‡ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΡƒΠ»ΡƒΡ‡ΡˆΠ°ΡŽΡ‚ Ρ‚ΠΈΠΏΠΎΠ±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎΡΡ‚ΡŒ, позволяя компилятору Π²Ρ‹ΡΠ²Π»ΡΡ‚ΡŒ ошибки Π½Π° этапС компиляции ΠΈ гарантируя, Ρ‡Ρ‚ΠΎ всС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Π΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ ΠΊΠΎΡ€Ρ€Π΅ΠΊΡ‚Π½ΠΎ. Они ΠΏΠΎΠ²Ρ‹ΡˆΠ°ΡŽΡ‚ Ρ‡ΠΈΡ‚Π°Π΅ΠΌΠΎΡΡ‚ΡŒ ΠΈ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅ΠΌΠΎΡΡ‚ΡŒ ΠΊΠΎΠ΄Π° Π·Π° счСт явного опрСдСлСния Ρ„ΠΎΡ€ΠΌ Π΄Π°Π½Π½Ρ‹Ρ… ΠΈ упрощСния условной Π»ΠΎΠ³ΠΈΠΊΠΈ. ΠšΡ€ΠΎΠΌΠ΅ Ρ‚ΠΎΠ³ΠΎ, Ρ‚Π°ΠΊΠΈΠ΅ ΠΏΡ€ΠΎΠ΄Π²ΠΈΠ½ΡƒΡ‚Ρ‹Π΅ Ρ‚Π΅Ρ…Π½ΠΈΠΊΠΈ, ΠΊΠ°ΠΊ ΠΈΡΡ‡Π΅Ρ€ΠΏΡ‹Π²Π°ΡŽΡ‰Π°Ρ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° с использованиСм Ρ‚ΠΈΠΏΠ° never, Π΄Π΅Π»Π°ΡŽΡ‚ ΠΊΠΎΠ΄ Π±ΠΎΠ»Π΅Π΅ Π½Π°Π΄Π΅ΠΆΠ½Ρ‹ΠΌ, прСдотвращая ΠΏΡ€ΠΎΠΏΡƒΡ‰Π΅Π½Π½Ρ‹Π΅ сцСнарии.

Π’Π½Π΅Π΄Ρ€Π΅Π½ΠΈΠ΅ Tagged Unions Π² Π²Π°ΡˆΡƒ ΠΏΠΎΠ²ΡΠ΅Π΄Π½Π΅Π²Π½ΡƒΡŽ ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΡƒ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Π½Π° TypeScript β€” это инвСстиция Π² качСство ΠΊΠΎΠ΄Π°, которая ΠΌΠ½ΠΎΠ³ΠΎΠΊΡ€Π°Ρ‚Π½ΠΎ окупаСтся сниТСниСм Π·Π°Ρ‚Ρ€Π°Ρ‚ Π½Π° ΠΎΡ‚Π»Π°Π΄ΠΊΡƒ, ускорСниСм Ρ€Π΅Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ½Π³Π° ΠΈ ΠΏΠΎΠ²Ρ‹ΡˆΠ΅Π½ΠΈΠ΅ΠΌ ΡΡ‚Π°Π±ΠΈΠ»ΡŒΠ½ΠΎΡΡ‚ΠΈ вашСго прилоТСния. ΠŸΠΎΠ΄Π΅Π»ΠΈΡ‚Π΅ΡΡŒ своим ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ Π² коммСнтариях – Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, Ρƒ вас Π΅ΡΡ‚ΡŒ свои ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹Π΅ сцСнарии использования Tagged Unions!