DevSurge 💦

TypeScript Utility Types — вспомогательные типы и области их применения

Cover Image for TypeScript Utility Types — вспомогательные типы и области их применения
Mark Nelyubin
Mark Nelyubin

В статье расскажу, что такое Utility Types в TypeScript, расскажу про основные вспомогательные типы, и покажу, как применять их на реальных проектах.

Вспомогательные типы (utility types) TypeScript - это набор встроенных generic-типов, которые можно использовать для преобразования или манипулирования другими типами.

Какие существуют вспомогательные типы

Partial

Partial<Type> — этот вспомогательный тип создает новый тип, делая все свойства исходного типа опциональными.

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

type PartialUser = Partial<User>;

// Result: { name?: string; age?: number; email?: string; }

const user: User = {
  name: 'Peter',
  age: 30,
  email: 'peter@gmail.com'
}

const partialUser: PartialUser = {
  email: 'user@gmail.com'
}

Required

Required<Type> — создает новый тип, делая все свойства исходного типа обязательными.

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

type RequiredUser = Required<User>;

// Result: { name: string; age: number; email: string; }

const user: User = {
  name: 'Peter',
}

const partialUser: RequiredUser = {
  name: 'user',
  age: 18,
  email: 'user@gmail.com'
}

Readonly

Readonly<Type> — создает новый тип, делая все свойства исходного типа доступными только для чтения, но не для модификации.

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

type ReadonlyUser = Readonly<User>;
// Result: { readonly name: string; readonly age: number; readonly email: string; }

const user: User = {
  name: 'Peter',
  age: 30,
  email: 'peter@gmail.com'
}

user.name = 'Sveta';

const strictUser: ReadonlyUser = {
  name: 'user',
  age: 18,
  email: 'user@gmail.com'
}

strictUser.name = 'Andrey'; // Cannot assign to 'name' because it is a read-only property.

Pick

Pick<Type, Keys> — создает новый тип путем выбора набора свойств из исходного типа на основе параметра Keys.

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

type UserSocial = Pick<User, 'name' | 'email'>;

// Result: { name: string; email: string; }

const userSocial: UserSocial = {
  name: "Vlad",
  email: 'vadiyha@list.ru'
}

Omit

Omit<Type, Keys> — создает новый тип путем исключения набора свойств из исходного типа на основе параметра Keys

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

type UserSocial = Omit<User, 'age'>;

// Result: { name: string; email: string; }

const userSocial: UserSocial = {
  name: "Vlad",
  email: 'vadiyha@list.ru'
}

Parameters

Parameters<T> — создает tuple type из типов, используемых в параметрах функции типа T

type FuncParams0 = Parameters<(s: string) => void>; // [s: string]

function myFunction(name: string, age: number): string {
  return 'Hello, world!';
}

type FuncParams1 = Parameters<typeof myFunction>; // [name: string, age: number]

ReturnType

ReturnType<T> — создает тип, состоящий из возвращаемого типа функции Type.

type MyFunctionReturnType1 = ReturnType<() => number>; // type is 'number'

function myFunction(): string {
  return 'Hello, world!';
}

type MyFunctionReturnType = ReturnType<typeof myFunction>; // type is 'string'

Exclude

Exclude<Type, ExcludedUnion> — создает новый тип путем исключения из типа любых типов в ExcludedUnion из Type.

type MyUnion = 'a' | 'b' | 'c' | 'd';
type MyExcludedType = Exclude<MyUnion, 'a' | 'b'>;
// Result: 'c' | 'd'

const firstLetter: MyUnion = 'a';
// const secondLetter: MyExcludedType = 'b'; // Type '"b"' is not assignable to type 'MyExcludedType'
const lastLetter: MyExcludedType = 'd';

Record

Record<KeyType, ValueType> — создает новый тип с набором свойств, определяемых KeyType, и соответствующим типом значения, определяемым ValueType

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

type UserRecord = Record<string, User>;

// Result: { [x: string]: User }

const users: UserRecord = {
  userOne: {
    name: 'Peter',
    age: 50,
    email: 'peter@mail.ru',
  },
  userTwo: {
    name: 'Vladlena',
    age: 89,
    email: 'edinorozhka@mail.ru',
  }
}

Примеры с реальных проектов

Библиотека React имеет собственные вспомогательные типы, которые базируются на TypeScript utlity types:

  • React.ComponentPropsWithRef использует Partial<T>, чтобы сделать все свойства опциональными
  • React.FC использует Required<T>, чтобы сделать все свойства обязательными
interface MyProps {
  name: string;
  age: number;
  address: string;
}

type MyPropsPartial = Partial<MyProps>;
type MyPropsRequired = Required<MyProps>;

Менеджер состояния Redux использует Record для определения стандартного корневого состояния:

type DefaultRootState = Record<string, any>;

В каких случаях утилитраные типы могут помочь на вашем проекте:

  • Типы Props и State: чтобы изменить тип свойств объекта, можно использовать Partial, Required, Pick и Omit.
import React, { FC } from 'react';

type Props = {
  foo?: string;
}

const MyComponent: FC<Props> = ({ foo = 'default' }) => {
  return (
    <div>{foo}</div>
  );
}

type RequiredProps = Required<Props>; // { foo: string }
type OptionalProps = Partial<Props>; // { foo?: string }
type OmittedProps = Omit<Props, 'foo'>; // {}

export default MyComponent;
  • Параметры функци и Return Types: при определении параметров функций и типов возвращаемых значений могут пригодиться ReturnType, Parameters и Required
type MyObject = {
  foo: string;
  bar: number;
}

function myFunction(param1: string, param2: number): MyObject {
  return {
    foo: param1,
    bar: param2,
  };
}

type ReturnTypeExample = ReturnType<typeof myFunction>; // { foo: string, bar: number }
type ParametersExample = Parameters<typeof myFunction>; // [string, number]
type RequiredParamsExample = Required<Parameters<typeof myFunction>>; // [string, number]
  • Generic типы: вспомогательные типы можно использовать вместе с generic типами для создания многократно используемого кода, который является гибким и адаптируемым. Например, вы можете использовать Pick и Exclude для создания общего типа, который извлекает из объекта определенные свойства и исключает другие.
type MyObject = {
  foo: string;
  bar: number;
  baz: boolean;
}

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
}

type MyExclude<T, U> = T extends U ? never : T;

type MyNewObject = MyPick<MyObject, 'foo' | 'baz'>; // { foo: string, baz: boolean }
type MyExcludedObject = MyExclude<keyof MyObject, 'bar'>; // 'foo' | 'baz'

Итого

  • Вспомогательные типы часто используются на реальных проектах, в частности, в различных библиотеках React;
  • Для работы с объектами чаще всего используются Partial, Required, Pick и Omit;
  • Для работы с функциями — ReturnType, Parameters и Required;

Другие материалы

Cover Image for Принципы SOLID с примерами на JS и Vue

Принципы SOLID с примерами на JS и Vue

Расскажу про принципы SOLID с актуальными примерами на JavaScript, Vue, React.

Mark Nelyubin
Mark Nelyubin
Cover Image for TypeScript Interface vs Type Aliases

TypeScript Interface vs Type Aliases

В чем разница между интерфейсом и псевдонимом типа? Когда использовать первое, а когда второе? Рассмотрю схожие черты, различия, дам рекомендации по использованию

Mark Nelyubin
Mark Nelyubin