← Назад к вопросам
Как переопределить тип?
1.7 Middle🔥 141 комментариев
#Soft Skills и рабочие процессы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Переопределение типов в TypeScript
Переопределение (override) типов в TypeScript это важный навык для работы с наследованием, интерфейсами и обобщённым программированием.
Type Narrowing: сужение типов
// Исходный широкий тип
let value: string | number | boolean;
// Сужаем тип через проверки
if (typeof value === 'string') {
// Здесь value это string
console.log(value.toUpperCase());
}
if (typeof value === 'number') {
// Здесь value это number
console.log(value.toFixed(2));
}
Type Guards с пользовательскими функциями
interface User {
name: string;
email: string;
}
interface Admin extends User {
role: 'admin';
permissions: string[];
}
// Type guard функция
function isAdmin(user: User): user is Admin {
return 'role' in user && user.role === 'admin';
}
// Использование
function handleUser(user: User | Admin) {
if (isAdmin(user)) {
// Здесь TypeScript знает, что это Admin
console.log('Admin permissions:', user.permissions);
} else {
console.log('Regular user:', user.name);
}
}
Переопределение типов через as (type assertion)
// Явное указание типа (нужно быть осторожным!)
const value: unknown = '123';
const stringValue = value as string;
const numberValue = Number(stringValue);
// Угловые скобки (альтернативный синтаксис)
const anotherValue = <string>value;
// Лучший подход - проверить перед переопределением
function safeTypeAssertion(value: unknown): string {
if (typeof value === 'string') {
return value;
}
throw new Error('Expected string');
}
Переопределение в классах (Override keyword)
class Animal {
speak() {
return 'Some sound';
}
}
class Dog extends Animal {
// @override помощь для IDE (не стандартный TypeScript)
speak() {
return 'Woof!';
}
}
const dog = new Dog();
console.log(dog.speak()); // "Woof!"
Обобщённые типы (Generics) и их переопределение
// Базовый обобщённый тип
interface Container<T> {
value: T;
getValue(): T;
}
// Переопределяем для конкретного типа
interface StringContainer extends Container<string> {
toUpperCase(): string;
}
// Реализация
class MyStringContainer implements StringContainer {
value: string = '';
getValue(): string {
return this.value;
}
toUpperCase(): string {
return this.value.toUpperCase();
}
}
// Использование
const container: StringContainer = new MyStringContainer();
container.value = 'hello';
console.log(container.toUpperCase()); // "HELLO"
Переопределение свойств в интерфейсах
// Базовый интерфейс
interface Shape {
color: string;
getArea(): number;
}
// Переопределяем в расширении
interface Circle extends Shape {
color: 'red' | 'blue' | 'green'; // Сужаем тип
radius: number;
}
// Использование
const circle: Circle = {
color: 'red',
radius: 5,
getArea() {
return Math.PI * this.radius * this.radius;
}
};
Utility типы для переопределения
interface User {
id: number;
name: string;
email: string;
created: Date;
}
// Partial - делаем все поля опциональными
type PartialUser = Partial<User>;
// Required - делаем все поля обязательными
type RequiredUser = Required<PartialUser>;
// Pick - выбираем нужные поля
type UserPreview = Pick<User, 'id' | 'name'>;
// Omit - исключаем поля
type UserUpdate = Omit<User, 'id' | 'created'>;
// Record - создаём тип с перечислением ключей
type UserRoles = Record<'admin' | 'user' | 'guest', User>;
// Readonly - делаем поля неизменяемыми
type ReadonlyUser = Readonly<User>;
// Extract - выбираем совпадающие типы из union
type StringOrNumber = string | number | boolean;
type OnlyString = Extract<StringOrNumber, string>; // string
Переопределение в React компонентах
import React from 'react';
// Базовый интерфейс
interface BaseButtonProps {
variant: 'primary' | 'secondary';
onClick: () => void;
}
// Расширяем для конкретного варианта
interface PrimaryButtonProps extends BaseButtonProps {
variant: 'primary';
size: 'small' | 'medium' | 'large';
}
interface SecondaryButtonProps extends BaseButtonProps {
variant: 'secondary';
outline: boolean;
}
type ButtonProps = PrimaryButtonProps | SecondaryButtonProps;
export function Button(props: ButtonProps) {
if (props.variant === 'primary') {
return (
<button className={`btn-primary btn-${props.size}`}>
{props.children}
</button>
);
}
return (
<button className={props.outline ? 'btn-secondary-outline' : 'btn-secondary'}>
{props.children}
</button>
);
}
Conditional Types для переопределения
// Переопределяем тип в зависимости от условия
type Flatten<T> = T extends Array<infer U> ? U : T;
type A = Flatten<string[]>; // string
type B = Flatten<string>; // string
type C = Flatten<number[]>; // number
// Практический пример
interface ApiResponse<T> {
status: number;
data: T;
}
type ExtractData<T> = T extends ApiResponse<infer U> ? U : never;
const response: ApiResponse<string> = { status: 200, data: 'hello' };
type ResponseData = ExtractData<typeof response>; // string
Переопределение через intersection types
interface Timestamped {
created: Date;
updated: Date;
}
interface WithAuthor {
author: string;
authorId: number;
}
// Переопределяем через пересечение
type Post = {
title: string;
content: string;
} & Timestamped & WithAuthor;
// Эквивалентно
interface PostAlt extends Timestamped, WithAuthor {
title: string;
content: string;
}
const post: Post = {
title: 'My Post',
content: 'Lorem ipsum',
author: 'John',
authorId: 1,
created: new Date(),
updated: new Date(),
};
Best Practices при переопределении типов
- Используй type guards перед
asпереопределением - Избегай
any- это отменяет всю типизацию - Используй Utility типы вместо дублирования
- Расширяй интерфейсы через extends, не переписывай
- Проверяй типы с помощью
satisfiesоператора (TypeScript 4.9+)
// Плохо - нет проверки
const config: any = { apiUrl: 123 };
// Хорошо - проверяем тип
const config = { apiUrl: 'https://api.example.com' } satisfies Config;