Якщо ви використовуєте перевірку стилю захисту типу (==, ===, !=, !==) або switch на discriminant property (тут kind) TypeScript зрозуміє що об’єкт має бути типу, який має цей конкретний літерал, і додасть тип за вас :)
functionarea(s:Shape) {if (s.kind ==="square") {// Зараз TypeScript *знає* що `s` має бути square ;)// Тож ви можете безпечно використовувати його учасників :)returns.size *s.size; }else {// Це не square? TypeScript зрозуміє, що це має бути Rectangle ;)// Тож ви можете безпечно використовувати його учасників :)returns.width *s.height; }}
Exhaustive Checks
Вичерпні перевірки.
Досить часто ви хочете переконатися, що всі члени мають певний код
interfaceSquare { kind:"square"; size:number;}interfaceRectangle { kind:"rectangle"; width:number; height:number;}// Хтось щойно додав цей новий тип`Circle`// Ми хотіли б дозволити TypeScript видавати помилку в будь-якому місці, яке *needs* обробити цеinterfaceCircle { kind:"circle"; radius:number;}typeShape=Square|Rectangle|Circle;
Як приклад того, де все йде погано:
functionarea(s:Shape) {if (s.kind ==="square") {returns.size *s.size; }elseif (s.kind ==="rectangle") {returns.width *s.height; }// Було б чудово, якби ви могли змусити TypeScript видавати вам помилку?}
Ви можете зробити це, просто додавши пропуск і переконавшись, що виведений тип у цьому блоці сумісний із типом never. Наприклад, якщо ви додасте вичерпну перевірку, ви отримаєте гарну помилку:
functionarea(s:Shape) {if (s.kind ==="square") {returns.size *s.size; }elseif (s.kind ==="rectangle") {returns.width *s.height; }else {// ERROR : `Circle` не може бути зведений до `never`const_exhaustiveCheck:never= s; }}
Якщо використовується strictNullChecks і виконуються вичерпні перевірки, TypeScript може скаржитися, що «не всі шляхи коду повертають значення». Ви можете заглушити це, просто повернувши змінну _exhaustiveCheck (типу never). Так:
Ви можете написати функцію, яка приймає never (і тому може бути викликана лише зі змінною, яка виводиться як never), а потім видає помилку, якщо її тіло коли-небудь виконується:
functionassertNever(x:never):never {thrownewError('Unexpected value. Should have been never.');}
Приклад використання функції площі:
interfaceSquare { kind:"square"; size:number;}interfaceRectangle { kind:"rectangle"; width:number; height:number;}typeShape=Square|Rectangle;functionarea(s:Shape) {switch (s.kind) {case"square": returns.size *s.size;case"rectangle": returns.width *s.height;// Якщо під час компіляції додається новий випадок, ви отримаєте помилку компіляції// Якщо під час виконання з’являється нове значення, ви отримаєте помилку виконанняdefault: returnassertNever(s); }}
Retrospective Versioning
Скажімо, у вас є структура даних у формі:
typeDTO= { name:string}
І після того, як у вас є купа DTO, ви розумієте, що name було поганим вибором. Ви можете додати керування версіями ретроспективно, створивши новий union з literal number (або рядком, якщо хочете) DTO. Позначте версію 0 як undefined якщо у вас увімкнено strictNullChecks, це просто спрацює:
typeDTO=| { version:undefined,// version 0 name:string, }| { version:1, firstName:string, lastName:string,}// Even later | { version:2, firstName:string, middleName:string, lastName:string,} // So on
Популярною бібліотекою, яка використовує це, є redux.
Ось gist of reduxіз доданими анотаціями типу TypeScript:
import { createStore } from'redux'typeAction= { type:'INCREMENT' }| { type:'DECREMENT' }/*** Це редʼюсер, чиста функція з (стан, дія) => стан. * Описує, як дія перетворює початковий стан на наступний. * * Форма стану залежить від вас: це може бути примітив, масив, об’єкт, * або навіть структуру даних Immutable.js. Єдина важлива частина полягає в тому, що ви повинні * не змінювати об'єкт стану, але повертати новий об'єкт, якщо стан змінюється. * * У цьому прикладі ми використовуємо оператор `switch` і рядки, але ви можете використовувати помічник який * дотримується іншої конвенції (наприклад, функція map), якщо це має сенс для вас * демонструвати. */functioncounter(state =0, action:Action) {switch (action.type) {case'INCREMENT':return state +1case'DECREMENT':return state -1default:return state }}// Створення сховища Redux, що зберігає стан вашої програми.// Його API — { subscribe, dispatch, getState }.let store =createStore(counter)// Ви можете використовувати subscribe() для оновлення інтерфейсу користувача у відповідь на зміни стану.// Зазвичай ви використовуєте бібліотеку прив’язки перегляду (наприклад, React Redux), а не subscribe() безпосередньо.// Однак також може бути зручно зберегти поточний стан у localStorage.store.subscribe(() =>console.log(store.getState()))// Єдиний спосіб змінити внутрішній стан - це відправити action.// Аction можна серіалізувати, реєструвати або зберігати, а потім відтворювати.store.dispatch({ type:'INCREMENT' })// 1store.dispatch({ type:'INCREMENT' })// 2store.dispatch({ type:'DECREMENT' })// 1
Використання його з TypeScript забезпечує захист від друкарських помилок, покращує здатність до рефакторингу та самодокументування коду.