077 Вводное видео

Служебные типы - это упрощение тех сложных манипуляций с типами из прошлой главы. Они предоставляют готовый функционал для работы с типами в ТС

078 Partial, Required, Readonly

У нас есть есть три дженерика, которые позволяют быстро отредактировать модификаторы доступности и обязательности ключей.

  • Partial - делает все свойства в объекте необязательными для заполнения
  • Required - делает все свойства в объекте необязательными для заполнения
  • Readonly - все свойства объекта можно заполнить только при создании и далее только просматривать
  • Сочетание дженериков - можно комбинировать выбранные дженерики, чтобы модифицировать доступность более гибко
interface User {  
    name: string;  
    age?: number;  
    address: string;  
}  
 
// Partial
type partial = Partial<User>;  
const personPartial: partial = {}; // Нет обязательных полей  
  
 
// Required
type required = Required<User>;  
const personRequired: required = {  
    name: 'string',  
    age: 22, // Обязательно  
    address: 'string',  
};  
  
 
// Readonly
type readonlyP = Readonly<User>;  
const personReadonly: readonlyP = {  
    name: 'string',  
    age: 22,  
    address: 'string',  
}  
personReadonly.name = 'John'; // Error  
  
 
// Сочетание дженериков
type requiredReadonly = Required<Readonly<User>>;  
const personRequiredReadonly: requiredReadonly = {  
    name: 'string',  
    age: 22, // Обязательно  
    address: 'string',  
};  
personReadonly.name = 'John'; // Error

Все данные инструменты построены под капотом на мапах. Partial представляет из себя мапу, которая на все ключи накидывает необязательность. Required делает все ключи обязательными через “-?”. Readonly уже навешивает на все ключи readonly модификатор

079 Pick, Omit, Extract, Exclude

Первые два дженерика - Omit и Pick - представляют из себя мапы, которые возвращают нам только нужные значения интерфейса в тип.

  • Omit - выкидывает ненужное свойство из интерфейса и хранит в тайпе результат модификации
  • Pick - берёт только нужные значения из интерфейса
interface PaymentPersistence {  
    id: number,  
    sum: number,  
    from: string,  
    to: string,  
}  
  
type PaymentWithoutId = Omit<PaymentPersistence, 'id'>; // выкинет свойство id  
type PaymentRequisits = Pick<PaymentPersistence, 'from' | 'to'>; // Берёт только 'from' и 'to'

Далее уже идут дженерики, основанные на кондишеналах внутри.

  • Extract - вытаскивает из вложенного юнион-типа только те свойства, которые удовлетворяют типу данных, который был передан вторым аргументом.
  • Exclude - исключает из юнион-тайпа все те значения, которые равны указанному типу во втором аргументе

Конкретно в нашем примере, Extract говорит, что нам нужно достать только свойства, тип которых будет строкой

interface PaymentPersistence {  
    id: number,  
    sum: number,  
    from: string,  
    to: string,  
}  
  
type PaymentWithoutId = Omit<PaymentPersistence, 'id'>;
type PaymentRequisits = Pick<PaymentPersistence, 'from' | 'to'>;
 
// sum не попадёт в выборку
type ExtractEx = Extract<'from' | 'to' | PaymentWithoutId, string>; 
 
// В выборку попадёт только PaymentWithoutId, а 'from' и 'to' будут исключены
type ExcludeEx = Exclude<'from' | 'to' | PaymentWithoutId, string>;

И так выглядят эти дженерики под капотом

080 ReturnType, Parameters, ConstructorParameters

И дальше у нас идут дженерики, которые позволяют достать типы из функции.

  • ReturnType<> - возвращает тип возвращаемого значения из функции
  • Parameters<> - возвращает кортеж типов аргументов функции
class User {  
    constructor(  
        public id: number,  
        public name: string  
    ) {  
    }
}  
  
function getData(id: number): User {  
    return new User(id, 'John');  
}  
 
// Вернёт то, что возвращает функция
type RT = ReturnType<typeof getData>; // User 
type RT2 = ReturnType<() => void>; // void
type RT3 = ReturnType<<T>() => T>; // unknown
type RT4 = ReturnType<<T extends string>() => T>; // string
 
// Вернёт то, что находится в параметрах функции в виде кортежа
type PT = Parameters<typeof getData>; // === [id:number]
type first = PT[0]; // === number

Так же доступна и такая запись для быстрого обращения к первому параметру

type PT = Parameters<typeof getData>[0]; 

И так же у нас есть подобные дженерики для работы с классами:

  • ConstructorParameters - возвращает кортеж параметров конструктора класса
  • InstanceType - вернёт тип инстанса
class User {  
    constructor(  
        public id: number,  
        public name: string  
    ) {  
    }
}  
 
// [id: number, name: string]
type CP = ConstructorParameters<typeof User>;
 
// User
type IT = InstanceType<typeof User>;

081 Awaited

Дженерик Awaited<> показывает нам полный результат ожидания Promise - а именно тот тип, который должен вернуть Promise

type A = Promise<string>; // Promise<string>
type A2 = Awaited<Promise<string>>; // string
type A3 = Awaited<Promise<Promise<string>>>; // string

И примерно так выглядит Awaited изнутрянки. Тут уже используются вложенные кондишенелы для реализации проверки. Тут уже через условия и рекурсию реализована возможность получать конечный возвращаемый тип промиса и его вложений

Первый кейс использования: нам нужно вернуть с бэка на фронт менюшку и поработать с ней. Конкретно тут удобство заключается в том, что мы можем поработать с этой менюшкой, как с определённым типом данных. ReturnType вернёт нам Promise<IMenu[]>, а уже сам Awaited вернёт IMenu[]

interface IMenu {
    name: string;
    url: string;
}
 
async function getMenu(): Promise<IMenu[]> {
    return [
        {
            name: 'Analythics',
            url: 'http...'
        }
    ];
}
 
type R = Awaited<ReturnType<typeof getMenu>>; // IMenu[]

Ну и так же в этом примере представлен второй кейс использования авэйтеда. Он тут в качестве типа подставляется автоматически в качестве указания типа ретёрна из функции. Тут возвращается массив результатов асинхронных функций

async function getArray<T>(x: T) {
    return [await x];
}