Union type означает «значение одного из типов»:
type StringOrNumber = string | number
function format(value: StringOrNumber): string {
if (typeof value === 'string') {
return value.toUpperCase() // TS знает: здесь string
}
return value.toFixed(2) // TS знает: здесь number
}
format('hello') // 'HELLO'
format(3.14159) // '3.14'TypeScript автоматически **сужает тип** (type narrowing) внутри блоков if/else, switch.
Intersection type означает «значение всех типов одновременно» — объект должен иметь все поля:
interface Named {
name: string
}
interface Aged {
age: number
}
type Person = Named & Aged // имеет И name, И age
const p: Person = { name: 'Алексей', age: 30 } // OK
// const bad: Person = { name: 'Алексей' } // Ошибка: нет age
// Полезно для миксинов:
type AdminUser = User & { permissions: string[] }Самый мощный паттерн TypeScript — добавляем поле-«тег» (обычно type или kind) для различения вариантов:
type Circle = { kind: 'circle'; radius: number }
type Rectangle = { kind: 'rect'; w: number; h: number }
type Triangle = { kind: 'triangle'; base: number; height: number }
type Shape = Circle | Rectangle | Triangle
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2 // TS знает: Circle
case 'rect':
return shape.w * shape.h // TS знает: Rectangle
case 'triangle':
return shape.base * shape.height / 2
}
}TypeScript может проверить что все случаи обработаны:
function assertNever(value: never): never {
throw new Error(`Необработанный случай: ${JSON.stringify(value)}`)
}
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2
case 'rect': return shape.w * shape.h
case 'triangle': return shape.base * shape.height / 2
default:
return assertNever(shape)
// Если добавить новый вид Shape и забыть обработать —
// TypeScript выдаст ошибку компиляции на этой строке!
}
}// API ответ — классический discriminated union
type ApiResponse<T> =
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; message: string }
function render<T>(response: ApiResponse<T>) {
switch (response.status) {
case 'loading': return 'Загрузка...'
case 'success': return JSON.stringify(response.data)
case 'error': return `Ошибка: ${response.message}`
}
}
// Redux actions — тоже discriminated union
type Action =
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'SET'; value: number }
function reducer(state: number, action: Action): number {
switch (action.type) {
case 'INCREMENT': return state + 1
case 'DECREMENT': return state - 1
case 'SET': return action.value // TS знает: есть value
}
}// Базовые интерфейсы:
interface Timestamps {
createdAt: Date
updatedAt: Date
}
interface SoftDelete {
deletedAt: Date | null
}
// Модель с временными метками и мягким удалением:
type UserModel = User & Timestamps & SoftDelete
// Partial intersection для обновлений:
type UserUpdate = Partial<User> & { updatedAt: Date }Discriminated union для фигур: getArea и describe с switch по kind, exhaustive check
// В TypeScript:
// type Shape = {kind:'circle', radius:number} | {kind:'rect', w:number, h:number} | ...
// В JS работаем с теми же объектами — просто без аннотаций типов
function getArea(shape) {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'rect':
return shape.w * shape.h
case 'triangle':
return shape.base * shape.height / 2
default:
// Exhaustive check — в TS здесь было бы assertNever(shape)
throw new Error(`Неизвестный вид фигуры: ${shape.kind}`)
}
}
function getPerimeter(shape) {
switch (shape.kind) {
case 'circle': return 2 * Math.PI * shape.radius
case 'rect': return 2 * (shape.w + shape.h)
case 'triangle': return shape.a + shape.b + shape.c
default:
throw new Error(`Неизвестный вид фигуры: ${shape.kind}`)
}
}
function describe(shape) {
const area = getArea(shape).toFixed(2)
switch (shape.kind) {
case 'circle':
return `Круг (r=${shape.radius}): площадь ${area}`
case 'rect':
return `Прямоугольник (${shape.w}×${shape.h}): площадь ${area}`
case 'triangle':
return `Треугольник (основ=${shape.base}, выс=${shape.height}): площадь ${area}`
default:
throw new Error(`Неизвестный вид фигуры: ${shape.kind}`)
}
}
// Фабричные функции — удобнее чем писать объект вручную
const circle = (radius) => ({ kind: 'circle', radius })
const rect = (w, h) => ({ kind: 'rect', w, h })
const triangle = (base, height, a = base, b = base, c = base) =>
({ kind: 'triangle', base, height, a, b, c })
// --- Демонстрация ---
const shapes = [
circle(5),
rect(4, 6),
triangle(8, 3),
circle(1),
rect(10, 2),
]
console.log('=== Описание фигур ===')
shapes.forEach(s => console.log(describe(s)))
console.log('\n=== Сортировка по площади ===')
const sorted = [...shapes].sort((a, b) => getArea(a) - getArea(b))
sorted.forEach(s => {
console.log(` ${s.kind}: ${getArea(s).toFixed(2)}`)
})
console.log('\n=== Статистика по типам ===')
const grouped = shapes.reduce((acc, s) => {
acc[s.kind] = (acc[s.kind] || 0) + 1
return acc
}, {})
Object.entries(grouped).forEach(([kind, count]) => {
console.log(` ${kind}: ${count} шт.`)
})
console.log('\n=== Exhaustive check ===')
try {
getArea({ kind: 'hexagon', side: 5 })
} catch (e) {
console.log(`Ошибка: ${e.message}`)
}Union type означает «значение одного из типов»:
type StringOrNumber = string | number
function format(value: StringOrNumber): string {
if (typeof value === 'string') {
return value.toUpperCase() // TS знает: здесь string
}
return value.toFixed(2) // TS знает: здесь number
}
format('hello') // 'HELLO'
format(3.14159) // '3.14'TypeScript автоматически **сужает тип** (type narrowing) внутри блоков if/else, switch.
Intersection type означает «значение всех типов одновременно» — объект должен иметь все поля:
interface Named {
name: string
}
interface Aged {
age: number
}
type Person = Named & Aged // имеет И name, И age
const p: Person = { name: 'Алексей', age: 30 } // OK
// const bad: Person = { name: 'Алексей' } // Ошибка: нет age
// Полезно для миксинов:
type AdminUser = User & { permissions: string[] }Самый мощный паттерн TypeScript — добавляем поле-«тег» (обычно type или kind) для различения вариантов:
type Circle = { kind: 'circle'; radius: number }
type Rectangle = { kind: 'rect'; w: number; h: number }
type Triangle = { kind: 'triangle'; base: number; height: number }
type Shape = Circle | Rectangle | Triangle
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2 // TS знает: Circle
case 'rect':
return shape.w * shape.h // TS знает: Rectangle
case 'triangle':
return shape.base * shape.height / 2
}
}TypeScript может проверить что все случаи обработаны:
function assertNever(value: never): never {
throw new Error(`Необработанный случай: ${JSON.stringify(value)}`)
}
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle': return Math.PI * shape.radius ** 2
case 'rect': return shape.w * shape.h
case 'triangle': return shape.base * shape.height / 2
default:
return assertNever(shape)
// Если добавить новый вид Shape и забыть обработать —
// TypeScript выдаст ошибку компиляции на этой строке!
}
}// API ответ — классический discriminated union
type ApiResponse<T> =
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; message: string }
function render<T>(response: ApiResponse<T>) {
switch (response.status) {
case 'loading': return 'Загрузка...'
case 'success': return JSON.stringify(response.data)
case 'error': return `Ошибка: ${response.message}`
}
}
// Redux actions — тоже discriminated union
type Action =
| { type: 'INCREMENT' }
| { type: 'DECREMENT' }
| { type: 'SET'; value: number }
function reducer(state: number, action: Action): number {
switch (action.type) {
case 'INCREMENT': return state + 1
case 'DECREMENT': return state - 1
case 'SET': return action.value // TS знает: есть value
}
}// Базовые интерфейсы:
interface Timestamps {
createdAt: Date
updatedAt: Date
}
interface SoftDelete {
deletedAt: Date | null
}
// Модель с временными метками и мягким удалением:
type UserModel = User & Timestamps & SoftDelete
// Partial intersection для обновлений:
type UserUpdate = Partial<User> & { updatedAt: Date }Discriminated union для фигур: getArea и describe с switch по kind, exhaustive check
// В TypeScript:
// type Shape = {kind:'circle', radius:number} | {kind:'rect', w:number, h:number} | ...
// В JS работаем с теми же объектами — просто без аннотаций типов
function getArea(shape) {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2
case 'rect':
return shape.w * shape.h
case 'triangle':
return shape.base * shape.height / 2
default:
// Exhaustive check — в TS здесь было бы assertNever(shape)
throw new Error(`Неизвестный вид фигуры: ${shape.kind}`)
}
}
function getPerimeter(shape) {
switch (shape.kind) {
case 'circle': return 2 * Math.PI * shape.radius
case 'rect': return 2 * (shape.w + shape.h)
case 'triangle': return shape.a + shape.b + shape.c
default:
throw new Error(`Неизвестный вид фигуры: ${shape.kind}`)
}
}
function describe(shape) {
const area = getArea(shape).toFixed(2)
switch (shape.kind) {
case 'circle':
return `Круг (r=${shape.radius}): площадь ${area}`
case 'rect':
return `Прямоугольник (${shape.w}×${shape.h}): площадь ${area}`
case 'triangle':
return `Треугольник (основ=${shape.base}, выс=${shape.height}): площадь ${area}`
default:
throw new Error(`Неизвестный вид фигуры: ${shape.kind}`)
}
}
// Фабричные функции — удобнее чем писать объект вручную
const circle = (radius) => ({ kind: 'circle', radius })
const rect = (w, h) => ({ kind: 'rect', w, h })
const triangle = (base, height, a = base, b = base, c = base) =>
({ kind: 'triangle', base, height, a, b, c })
// --- Демонстрация ---
const shapes = [
circle(5),
rect(4, 6),
triangle(8, 3),
circle(1),
rect(10, 2),
]
console.log('=== Описание фигур ===')
shapes.forEach(s => console.log(describe(s)))
console.log('\n=== Сортировка по площади ===')
const sorted = [...shapes].sort((a, b) => getArea(a) - getArea(b))
sorted.forEach(s => {
console.log(` ${s.kind}: ${getArea(s).toFixed(2)}`)
})
console.log('\n=== Статистика по типам ===')
const grouped = shapes.reduce((acc, s) => {
acc[s.kind] = (acc[s.kind] || 0) + 1
return acc
}, {})
Object.entries(grouped).forEach(([kind, count]) => {
console.log(` ${kind}: ${count} шт.`)
})
console.log('\n=== Exhaustive check ===')
try {
getArea({ kind: 'hexagon', side: 5 })
} catch (e) {
console.log(`Ошибка: ${e.message}`)
}Реализуй API response паттерн с discriminated union. Функции-конструкторы: `loading()` → `{status:"loading"}`, `success(data)` → `{status:"success", data}`, `error(message)` → `{status:"error", message}`. Функция `handleResponse(response)` возвращает строку: для loading — "Загрузка...", для success — "Данные: " + JSON.stringify(data), для error — "Ошибка: " + message. Функция `isSuccess(response)` возвращает boolean.
Конструкторы просто возвращают объекты с полем status. В handleResponse используй switch (response.status). isSuccess: return response.status === "success". Не забудь case "default" с throw в handleResponse.
Токены для AI-помощника закончились
Купи токены чтобы задавать вопросы AI прямо в уроке