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

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

1.7 Middle🔥 121 комментариев
#Soft Skills и рабочие процессы

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

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

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

Для чего нужен Pick в TypeScript

Pick - это utility type для создания нового типа, выбирая только определённые поля из существующего типа. Это полезно для переиспользования типов и избежания дублирования кода.

Базовое использование

interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  phone?: string;
  address?: string;
  age?: number;
}

// Создаём новый тип, выбирая только нужные поля
type UserPreview = Pick<User, 'id' | 'name'>;

// Это эквивалентно:
interface UserPreview {
  id: string;
  name: string;
}

const user: UserPreview = {
  id: '123',
  name: 'John'
};

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

1. Публичный профиль (без приватных данных)

interface User {
  id: string;
  name: string;
  email: string;
  password: string; // Не нужна в публичном профиле
  phone: string;
  isAdmin: boolean; // Не нужно показывать
}

// Профиль для API ответа
type UserPublicProfile = Pick<User, 'id' | 'name' | 'phone'>;

// Использование
function getUserProfile(userId: string): UserPublicProfile {
  const user = getUser(userId);
  return {
    id: user.id,
    name: user.name,
    phone: user.phone
  };
}

2. Форма редактирования (только редактируемые поля)

interface Article {
  id: string; // readonly
  title: string;
  content: string;
  author: string; // readonly
  createdAt: Date; // readonly
  updatedAt: Date; // readonly
  tags: string[];
  published: boolean;
}

// Только те поля, которые можно редактировать
type ArticleEditForm = Pick<Article, 'title' | 'content' | 'tags' | 'published'>;

function updateArticle(id: string, data: ArticleEditForm) {
  // data содержит только редактируемые поля
}

3. API запрос (только нужные поля)

interface CreateUserRequest {
  email: string;
  password: string;
  name: string;
  phone?: string;
}

interface User extends CreateUserRequest {
  id: string;
  createdAt: Date;
  isActive: boolean;
}

// Используем Pick для переиспользования типа
type UserCreateData = Pick<User, 'email' | 'password' | 'name' | 'phone'>;

// Или проще
type UserCreateData = Omit<User, 'id' | 'createdAt' | 'isActive'>;

4. React компоненты - передача части props

interface ButtonProps {
  onClick: () => void;
  disabled?: boolean;
  loading?: boolean;
  variant?: 'primary' | 'secondary';
  size?: 'small' | 'medium' | 'large';
  children: React.ReactNode;
  className?: string;
  title?: string;
  ariaLabel?: string;
}

// Для простых кнопок передаём только базовые props
type SimpleButtonProps = Pick<ButtonProps, 'onClick' | 'disabled' | 'children'>;

function SimpleButton(props: SimpleButtonProps) {
  return <button onClick={props.onClick} disabled={props.disabled}>{props.children}</button>;
}

// Для кнопки с полным функционалом используем все props
function AdvancedButton(props: ButtonProps) {
  return (
    <button 
      onClick={props.onClick}
      disabled={props.disabled || props.loading}
      className={`btn btn-${props.variant} btn-${props.size}`}
      title={props.title}
      aria-label={props.ariaLabel}
    >
      {props.loading ? 'Loading...' : props.children}
    </button>
  );
}

Pick vs Omit

Pick - выбираешь что ВКЛЮЧИТЬ Omit - выбираешь что ИСКЛЮЧИТЬ

interface Product {
  id: string;
  name: string;
  description: string;
  price: number;
  inStock: boolean;
  inventory: number;
  warehouse: string;
  supplier: string;
  createdAt: Date;
}

// Pick - для простого типа берём основные поля
type ProductCard = Pick<Product, 'id' | 'name' | 'price' | 'inStock'>;

// Omit - когда мало исключаемых полей
type ProductForDisplay = Omit<Product, 'inventory' | 'warehouse' | 'supplier' | 'createdAt'>;

// Результат один и тот же
const product1: ProductCard = { id: '1', name: 'Item', price: 100, inStock: true };
const product2: ProductForDisplay = { id: '1', name: 'Item', description: 'Desc', price: 100, inStock: true };

С функциями

interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  role: 'admin' | 'user';
}

// Функция берёт только нужные поля
function sendWelcomeEmail(user: Pick<User, 'email' | 'name'>) {
  console.log(`Sending email to ${user.name} at ${user.email}`);
}

const user: User = {
  id: '1',
  name: 'John',
  email: 'john@example.com',
  password: 'secret',
  role: 'user'
};

sendWelcomeEmail(user); // OK, Pick только требует email и name

// Функция явно говорит, что ей нужны только эти два поля
// Если в будущем удалим name, функция сломается (хорошо!)

С конструктором объектов

interface FullUser {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  address: string;
}

class UserService {
  // Создание пользователя требует только определённые данные
  create(data: Pick<FullUser, 'firstName' | 'lastName' | 'email'>) {
    // id и остальные поля генерируются сервером
    return {
      id: generateId(),
      ...data,
      phone: '',
      address: ''
    };
  }

  // Обновление - разные поля
  update(id: string, data: Pick<FullUser, 'firstName' | 'lastName' | 'phone'>) {
    // email и address не меняются
  }
}

Комбинирование Pick с другими типами

interface Article {
  id: string;
  title: string;
  content: string;
  author: string;
  tags: string[];
  published: boolean;
  views: number;
}

// Комбинируем Pick с расширением
type ArticlePreview = Pick<Article, 'id' | 'title' | 'author'> & {
  excerpt: string; // Добавляем новое поле
  readTime: number;
};

const preview: ArticlePreview = {
  id: '1',
  title: 'Article',
  author: 'John',
  excerpt: 'First 100 chars...',
  readTime: 5
};

// Или с Partial для опциональных полей
type ArticleUpdateData = Partial<Pick<Article, 'title' | 'content' | 'tags' | 'published'>>;

const updateData: ArticleUpdateData = {
  title: 'New Title'
  // Остальные поля опциональны
};

Реальный пример: OAuth

interface OAuthUser {
  id: string;
  email: string;
  name: string;
  avatar: string;
  provider: 'google' | 'github' | 'facebook';
  accessToken: string; // Приватная информация
  refreshToken: string; // Приватная информация
  expiresAt: Date; // Приватная информация
}

// Публичная информация пользователя
type PublicUserInfo = Pick<OAuthUser, 'id' | 'email' | 'name' | 'avatar'>;

function getPublicProfile(user: OAuthUser): PublicUserInfo {
  return {
    id: user.id,
    email: user.email,
    name: user.name,
    avatar: user.avatar
  };
}

function storeTokens(user: OAuthUser): Pick<OAuthUser, 'accessToken' | 'refreshToken'> {
  return {
    accessToken: user.accessToken,
    refreshToken: user.refreshToken
  };
}

Вывод

Pick используется для:

  1. Переиспользования типов - выбираешь нужные поля существующего типа
  2. Безопасности - скрываешь приватные данные в public API
  3. Ясности - функция явно показывает, какие данные ей нужны
  4. Поддерживаемости - если структура тип меняется, Pick автоматически адаптируется
  5. DRY принципу - не дублируешь определение одних и тех же полей

Правило: Если видишь дублирование полей в разных типах - используй Pick для переиспользования существующего типа.