← Назад к вопросам

Как переопределить тип?

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 при переопределении типов

  1. Используй type guards перед as переопределением
  2. Избегай any - это отменяет всю типизацию
  3. Используй Utility типы вместо дублирования
  4. Расширяй интерфейсы через extends, не переписывай
  5. Проверяй типы с помощью satisfies оператора (TypeScript 4.9+)
// Плохо - нет проверки
const config: any = { apiUrl: 123 };

// Хорошо - проверяем тип
const config = { apiUrl: 'https://api.example.com' } satisfies Config;