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

Для чего нужен Partial?

1.0 Junior🔥 171 комментариев
#TypeScript

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Для чего нужен Partial?

Partial<T> — это встроенный утилита-тип в TypeScript, который делает все свойства типа T опциональными (необязательными). Это полезно когда нужно обновить только часть объекта, не требуя заполнения всех полей.

Синтаксис и базовое использование

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// Все поля становятся опциональными
type PartialUser = Partial<User>;

// Эквивалентно:
type PartialUser = {
  id?: number;
  name?: string;
  email?: string;
  age?: number;
};

Практические примеры

Обновление пользователя (функция):

function updateUser(id: number, changes: Partial<User>): User {
  const user = users.find(u => u.id === id);
  if (!user) throw new Error("User not found");
  
  return { ...user, ...changes };
}

// Можно передать только нужные поля
updateUser(1, { name: "Алиса" });
updateUser(2, { email: "bob@example.com", age: 30 });
updateUser(3, {}); // Даже пустой объект - OK

API запрос для обновления:

interface Product {
  id: number;
  title: string;
  price: number;
  description: string;
  category: string;
}

// PATCH endpoint - обновляет частично
function patchProduct(
  id: number, 
  data: Partial<Product>
): Promise<Product> {
  return fetch(`/api/products/${id}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data)
  }).then(r => r.json());
}

// Использование
patchProduct(1, { title: "Новое название" });
patchProduct(2, { price: 99.99, category: "Electronics" });

React компонент с опциональными props:

interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled: boolean;
  size: "sm" | "md" | "lg";
  variant: "primary" | "secondary";
}

function mergeButtonProps(
  defaults: ButtonProps,
  overrides: Partial<ButtonProps>
): ButtonProps {
  return { ...defaults, ...overrides };
}

const defaultProps: ButtonProps = {
  label: "Click me",
  onClick: () => {},
  disabled: false,
  size: "md",
  variant: "primary"
};

const customProps = mergeButtonProps(defaultProps, {
  label: "Submit",
  variant: "secondary"
});

Форма с валидацией:

interface FormData {
  username: string;
  password: string;
  email: string;
  phone: string;
}

interface FormState {
  values: Partial<FormData>;
  errors: Partial<Record<keyof FormData, string>>;
}

const [form, setForm] = useState<FormState>({
  values: {},
  errors: {}
});

function updateFormField(field: keyof FormData, value: string) {
  setForm(prev => ({
    ...prev,
    values: { ...prev.values, [field]: value }
  }));
}

Сравнение с другими утилита-типами

Partial<T> - все поля опциональны:

type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; }

Required<T> - все поля обязательны:

type RequiredUser = Required<PartialUser>;
// { id: number; name: string; email: string; }

Pick<T, K> - выбрать конкретные поля:

type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string; }

Omit<T, K> - исключить поля:

type UserWithoutPassword = Omit<User, "password">;
// Все поля кроме password

Реальный пример: настройки приложения

interface AppSettings {
  theme: "light" | "dark";
  fontSize: number;
  notifications: boolean;
  language: "en" | "ru" | "de";
  autoSave: boolean;
  debugMode: boolean;
}

class SettingsManager {
  private defaults: AppSettings = {
    theme: "light",
    fontSize: 14,
    notifications: true,
    language: "en",
    autoSave: true,
    debugMode: false
  };
  
  // updateSettings принимает только те поля, которые нужно обновить
  updateSettings(overrides: Partial<AppSettings>): AppSettings {
    return { ...this.defaults, ...overrides };
  }
  
  getSetting<K extends keyof AppSettings>(
    key: K
  ): AppSettings[K] {
    return this.defaults[key];
  }
}

const settings = new SettingsManager();

// Обновляем только то, что нужно
settings.updateSettings({
  theme: "dark",
  fontSize: 16
});

Deep Partial (вложенный Partial)

interface DeepUser {
  id: number;
  profile: {
    name: string;
    bio: string;
    contacts: {
      email: string;
      phone: string;
    };
  };
}

// Обычный Partial не делает вложенные объекты опциональными
type PartialDeepUser = Partial<DeepUser>;
// profile.name всё ещё обязателен!

// Deep Partial
type DeepPartial<T> = T extends object ? {
  [P in keyof T]?: DeepPartial<T[P]>;
} : T;

type DeepPartialUser = DeepPartial<DeepUser>;
// Теперь всё опционально, даже вложенное

function updateDeepUser(changes: DeepPartial<DeepUser>) {
  // Может быть пустой объект
  // { profile: { contacts: { email: "new@example.com" } } }
}

Лучшие практики

  1. Используй для функций обновления:
// Хорошо
function update(id: number, partial: Partial<User>) { }

// Плохо
function update(id: number, user: User) { } // Требует все поля
  1. Используй для опций и конфигурации:
interface Config {
  timeout: number;
  retries: number;
  debug: boolean;
}

function createClient(options: Partial<Config>) {
  return { ...defaults, ...options };
}
  1. Комбинируй с другими утилита-типами:
// Выбрать поля и сделать опциональными
type PartialUserPreview = Partial<Pick<User, "name" | "email">>;

// Сделать опциональными, кроме одного
type AlmostPartialUser = Partial<User> & Pick<User, "id">;
  1. Используй для тестирования:
// Тестовый объект без всех требуемых полей
const mockUser: Partial<User> = {
  id: 1,
  name: "Test"
  // email не требуется в тесте
};

Partial<T> — это мощный инструмент для создания гибких типов, особенно полезный при работе с API, формами и обновлениями данных. Он делает код более безопасным и понятным.