Snippets
Links
https://www.typescriptlang.org/docs/handbook/utility-types.html
Basic types
// Define object
type Game= Record<string, unknown>
let some: Game = {zz: 11}, some2: Game = {zz:11, xx:22}
// Ternaries
type StringFromType<T> = T extends string ? 'string' : never
type lorem = StringFromType<'lorem ipsum'> // 'string'
type ten = StringFromType<10> // never
type NullableString = string | null | undefined
type NonNullable<T> = T extends null | undefined ? never : T // Built-in type, FYI
type CondUnionType = NonNullable<NullableString> // evalutes to `string`
Constant object for searching keys in another object with dynamic strings
const FIELDS = {
cardnum: 'cardnum',
cardmon: 'cardmon',
cardcvv: 'cardcvv',
} as const;
const messages = {
cardnumPlaceholder: { . . . },
cardmonPlaceholder: { . . . },
cardcvvPlaceholder: { . . . },
}
var message1 = messages[`${FIELDS.cardnum}Placeholder`]
var message2 = messages[`${FIELDS.cardmon}Placeholder`]
Как получить тип вложенного типа?
// Это пример хорошей организации типов
// Есть общий интрерфейс Person
interface Person {
name: string;
lastName: string;
id: string;
}
// Енам лицензий пилота
enum LicenseType {
STUDENT = "Student",
SPORT = "Sport",
COMMERCIAL = "Commercial",
FLIGHT_INSTRUCTOR = "FLIGHT Instructor"
}
// Сам пилот
interface Pilot extends Person {
licenseNumber: number;
// У него есть свойство licenseType с типом LicenseType
// Собирать тип из маленьких типов - хорошая практика
licenseType: LicenseType;
}
// Теперь аргумент функции мы также типизируем с LicenseType
const isCommercialLicense = (license: LicenseType) =>
license === LicenseType.COMMERCIAL;
const commercialPilot: Pilot = {
name: "Ivan",
lastName: "Petrov",
id: "a",
licenseNumber: 2022,
licenseType: LicenseType.COMMERCIAL
};
// Все красиво типизируется
console.log(isCommercialLicense(commercialPilot.licenseType));
// Теперь плохой пример
//
interface JetEngine {
model: {
name: string;
serialNumber: number;
type: "turbo" | "turbofan" | "ramjet";
};
// У нас теперь есть такой union-тип.
// По-хорошему такое нужно выносить в енам
state: "disabled" | "power-up" | "ready";
}
// Мы могли бы попытаться сделать так, но это было бы неправильно:
// const checkStateReadiness = (value: string) => value === "ready";
// const checkStateReadiness = (value: JetEngine.state) => value === "ready";
// Зато можно вытащить вложенный тип через скобочную запись
const checkStateReadiness = (value: JetEngine["state"]) => value === "ready";
// Теперь функция хорошо типизируется
console.log(checkStateReadiness("Bullshit"));
Generics?
// Функция каркас
function identity<Type>(argument: Type): Type {
return argument;
}
// Вызываем функцию с конкретным типом
const number = identity<number>(123);
interface MobileHouse {
model: string;
}
// А можем вызвать так
const mobileHouse = identity<MobileHouse>({ model: "Hymer" });
console.log(number, mobileHouse);
// Общий тип c дженериком Type
type APIResponse<Type> = {
items?: Type[]; // Данные могут быть в виде списка
item: Type; // Или одним элементом, если идет запрос по id
};
// Конкретный тип User
type User = {
id: string;
};
// fetchAPI - это тоже каркас.
// Принимает дженерик тип и возвращает промис с этим типом
const fetchAPI = <Type>(url: string): Promise<APIResponse<Type>> => {
return fetch(url).then((response) => response.json());
};
export const getUser = (id: string): Promise<User> => {
// В этой функции мы используем fetchAPI с конкретным типом User
return fetchAPI<User>(`/v1/user/${id}`).then(({ item }) => item);
};
Trim
type Chars = ' ' | '\n' | '\t'
type Trim<S extends string> = S extends `${Chars}${infer SS}` ? Trim<SS> : S extends `${infer SS}${Chars}` ? Trim<SS> : S
type str = Trim<' zzz '>
Как сужать тип?
type Employee = {
id: string;
name: string;
age: number;
sex: string;
dateOfBirth: Date;
// ... много чего еще
};
// Обычно мы делаем так:
// const getUserLabel = ({name, age}: Employee): string => {
// return `${age} ${name}`
// }
// Приходится использовать полный объект, когда как нужно всего пара полей
// console.log(getUserLabel({
// id: 'subscribe',
// name: 'Inna',
// age: 22,
// sex: 'female',
// dateOfBirth: new Date()
// }))
// as - костыль! Избегай его!
// console.log(getUserLabel({
// name: 'Inna',
// age: 22,
// } as Employee))
// Pick - утилита для сужения типов
// Передаем ей базовый тип Employee
// И юнион тип из тех значений, которые нам нужны в функции
const getUserLabel = ({
name,
age
}: Pick<Employee, "name" | "age">): string => {
return `${age} ${name}`;
};
// Теперь можем использовать объект из двух нужных полей
console.log(
getUserLabel({
name: "Inna",
age: 22
})
);
// Общий тип
type BaseType = {
a: number;
// общие свойства
};
// Получаем частный тип из общего с помощью сужения типов
export type CurrentType = Pick<BaseType, "a">;
Как keyof нам может помочь?
// Есть время истечения срока подписки
type ExpiryDateTime = {
days: number;
hours: number;
minutes: number;
seconds: number;
};
// Соответсвующий объект
let expiryDateTime: ExpiryDateTime = {
days: 0,
hours: 0,
minutes: 0,
seconds: 0
};
// Функция для изменения объекта по ключу
const change = (
key: keyof ExpiryDateTime, // Вот тут keyof создает нам юнион тип из свойств ExpiryDateTime
value: number,
expiryDateTime: ExpiryDateTime
): ExpiryDateTime => {
return { ...expiryDateTime, [key]: value };
};
expiryDateTime = change("seconds", 10, expiryDateTime);
console.log(expiryDateTime);
Как типизировать переменную возвращаемым значением функции?
let foo = "bar";
// typeof дает нам тип переменной
let name: typeof foo; // string
// Функция возвращает юнион тип из разных значений
const getHisOrHer = (value: number): "his" | "her" | "its" => {
if (value === 0) {
return "its";
}
return value % 2 === 0 ? "his" : "her";
};
// Утилита ReturnType помогает нам взять тип, который функция возвращает
const pronoun: ReturnType<typeof getHisOrHer> = getHisOrHer(3);
console.log(pronoun);
console.log(name);
Как маппить тип?
type RGB = {
r: number;
g: number;
b: number;
};
type Color = {
color1: RGB;
color2: RGB;
};
// Создавать клон - плохо
// type ColorCode = {
// color1: number;
// color2: number;
// };
// Такие самописные утилиты помогают нам мапить другие типы
type toNumberSwitch<Type> = {
[Property in keyof Type]: number; // Оператор in итерируется по значениям union-типа
};
export type toBooleanSwitch<Type> = {
[Property in keyof Type]: boolean;
};
export type toStringSwitch<Type> = {
[Property in keyof Type]: string;
};
const color: toNumberSwitch<Color> = {
color1: 1,
color2: 255
};
console.log(color);
Type form Object values
const OPERATORS = {
AND: { value: 'AND' },
OR: { value: 'OR' },
AND_NOT: { value: 'AND NOT' },
OR_NOT: { value: 'OR NOT' },
NOT: { value: 'NOT', unary: true },
} as const
type Keys = keyof Readonly<typeof OPERATORS> // "AND" | "OR" | "NOT" | "AND_NOT" | "OR_NOT"
type OperatorsType= typeof OPERATORS[Keys]['value']; // "AND" | "OR" | "AND NOT" | "OR NOT" | "NOT"
Standard utils implementation
Implement Pick<Type, Keys>
type CustomPick<T, K extends keyof T> = {
[Key in K]: T[Key]
}
Implement Omit<Type, Keys>
type CustomOmit<T,K extends keyof T> = {
[Key in keyof T as Key extends K ? never : Key] : T[Key]
}
Implement Extract<Type, Type>
type Extract<T, U> = T extends U ? T : never
type Exclude<T, U> = T extends U ? never : T
Implement ReturnType<Type>
type ReturnTypeCustom<T> = T extends (...args: any[]) => infer R ? R : any;
type b = ReturnTypeCustom<() => string | number> // string | number
Implement FnParameters<Type>
type FnParameters<T> = T extends (...args: infer R)=>any ? R : never
type params = FnParameters<(a1: number, b2: string)=>void>
Implement FirstArgOfFunc<Type>
type FirstArgOfFunc<T> = T extends (
first: infer FirstArgument,
...args: any[]
) => any ? FirstArgument : never
type t = FirstArgOfFunc<(name: string, age: number) => void> // string
Implement Array type
type ArrayType<T> = T extends (infer Item)[] ? Item : T
type t = ArrayType<[string, number]> // string | number
Last updated
Was this helpful?