Skip to main content

TypeScript в React

.d.ts

Декларация типов в файле

https://www.youtube.com/watch?v=_KweR3LUGRA


Простые переменные

const someString: string = 'Some stirng';
const someNumber: number = 123;
const someBoolean: boolean = false;

// объединение типов
const someUnknown: null | undefined = null;

Чем меньше any, тем лучше.


Переменныее для типов

type - нельзя переопределять. Нельзя расширять.

Все популярные библиотеки уже имеют свой готовый набор типов.

type KeyList = readonly string[];

// можно использовать в других типах
type KeyLists = KeyList[];

type Props = {
className: string;
count: number;
};

interface - нельзя объединять, но можно расширять.

interface Props {
className: string;
count: number;
}

Объекты

Объекты с определенным набором полей

type TObj = {
someString?: string; // опциональное
readonly someNumber: number; // неизменяемое
someBoolean: boolean;
someUnknown: null | undefined;
}

// 1 вариант
const someObj: TObj = {
someString: 'Some stirng',
someNumber: 123,
someBoolean: false,
someUnknown: null,
}

// 2 вариант
const someObj: {
someString?: string;
readonly someNumber: number;
someBoolean: boolean;
someUnknown: null | undefined;
} = {
someString: 'Some stirng',
someNumber: 123,
someBoolean: false,
someUnknown: null,
}

// 3 вариант (объект или null)
const courier: {
id: number;
firstName: string;
lastName: string;
middleName: string;
salaryType: number;
additionalTypeIds: number[];
} | null;

Объекты с неопределенным набором полей

// 1 вариант (Record)
const someUnknownObj: Record<string, number> = {
someField1: 1,
someField2: 2,
someField3: 3,
someField4: 4,
}

// 2 вариант (key)
const someUnknownObj: { [key: string]: number } = {
someField1: 1,
someField2: 2,
someField3: 3,
someField4: 4,
}

keyof

interface LikeButtonProps {
className: string;
count: number;
size: number;
}

type LikeButtonKeys = keyof LikeButtonProps; // 'className', 'count', 'size'

typeof

const props = {
className: 'Like',
count: 3,
size: 'L',
};

type LikeButtonProps = typeof props;

// ХОРОШО, для известных данных
const props: LikeButtonProps = {
className: 'Like',
count: 3,
size: 'L',
};

// ПЛОХО, но можно для входных данных
const props = {
className: 'Like',
count: 3,
size: 'L',
} as LikeButtonProps;

export type TStore = ReturnType<typeof store.getState>;

Типизация полей объекта

interface LikeButtonProps {
className: string;
count: number;
size: number;
}

const props: LikeButtonProps = {
className: 'Like',
count: 3,
size: 'L',
};

// типизация полей --> 'className', 'count', 'size'
type Keys = keyof IUsageDates;

// типизация зачений --> 'string' | 'number'
type Keys = keyof typeof props;
type Values = (typeof props)[Keys];

Пример

interface IUsageDates {
showDates: boolean;
startDate: Date | null;
endDate: Date | null;
}

// usageDates
const [usageDates, setUsageDates] = useState<IUsageDates>({
showDates: false,
startDate: null,
endDate: null,
});

const changeUsageDates = (
fieldName: keyof IUsageDates,
payload: (typeof usageDates)[keyof typeof usageDates],
) => {
setUsageDates({ ...usageDates, [fieldName]: payload });
}

Массивы

const someArray: number[] = [1, 2, 3, 4];
const someArray: string[] = ['asdf', 'cxvzx', 'rerer', 'fadsf'];

// через дженерик
const someArray: Array<number> = [1, 2, 3, 4];
const someArray: Array<string> = ['asdf', 'cxvzx', 'rerer', 'fadsf'];

// КОТРЕЖ, когда известно точное кол-во элементов
const someArray: [string, string, string, string] = ['asdf', 'cxvzx', 'rerer', 'fadsf'];

// только для чтения
const someArray: readonly string[] = ['asdf', 'cxvzx', 'rerer', 'fadsf'];
const someArray: ReadonlyArray<string> = ['asdf', 'cxvzx', 'rerer', 'fadsf'];
const someArray: readonly [string, string, string, string] = ['asdf', 'cxvzx', 'rerer', 'fadsf'];

Дженерик - тип, который зависит от других типов.


Any, void, unknown, never

any - игнорирование типизации; void - функция ничего не возвращает; unknown - неизвестный тип, может включать все типы одновременно never - тип, который не имеет значения в js

Функции

// function declaration
function() square(n: number): number {
return n * n;
}

// function expression
// сокращенная
const square = (n: number) => n * n;

// полная
const square: (n: number) => number = n => n * n;

Void

// возвращает number
(n: number) => number;

// ничего не возвращает
(n: number) => void;

// любые аргументы
(...args: any[]) => any;

Enum

Перечисления. Enumiration. Может быть одновременно и типом и значением. Компилируется в объект.

enum Size {
S,
M,
L,
XL,
}

Convert enum to array for maping

export enum DrugstoreView {
DRUGSTORE = 'Аптека',
HUB = 'Хаб',
VSP = 'ВСП',
LINK_TO_DISCONNECT = 'На отключение',
LINK_APPROVED = 'На подключение',
LINK_REJECT = 'Отказ от подключения',
}

// как любой объект
const drugstoreViews = Object.values(DrugstoreView);

FC

// children type
type TProps = {
children: React.ReactNode
}

Props

TODO


Events

HTMLInputElement / ChangeEvent

const onSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
console.log(value);
};

HTMLInputElement / FocusEvent

const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
const value = event.target.value;
console.log(value);
};

HTMLInputElement / FormEvent

const onLoginHandler = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();

if (email && password) {
let errors = await dispatch(login(email, password)) as unknown as boolean;
}
}

HTMLDivElement / MouseEvent

const onClick = (event: React.MouseEvent<HTMLDivElement>) => {
const value = event.target.value;
console.log(value);
}

Автоподстановка event

<UiRemoveIcon
title='Удалить'
width={15.75}
height={18.38}
color={hasAccess ? 'primary' : 'secondary'}

// полная запись
onClick={(e: React.MouseEvent<HTMLSpanElement>) => removeHandle(е)}

// можно записывать коротко без e
onClick={removeHandle}
/>

// метод-обработчик
const removeHandle = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
e.stopPropagation();

if (hasAccess) {
dispatch(setConfirmationOpen({
dialogType: 'positive',
dialogText: `Вы действительно хотите удалить шаблон ${data.name}?`,
confirmationFunction: () => {
dispatch(deleteExpressIntervalTemplateByUuid(data.uuid));
}
}))
}
}

DOM

const ref = useRef<HTMLDivElement>(null);

const ref2 = useRef<HTMLInputElement>(null);

Extends

interface LikeButtonProps {
className: string;
count: number;
}

interface CssProps {
className: string;
}

// подмешиваем поле count в интерфейс CssProps
interface LikeButtonProps extends CssProps {
count: number;
}

// 2 вариант - через type
type LikeButtonProps = CssProps & { count: number; }

Пример extends

import { Tooltip }               from '@material-ui/core';
import { useSelector } from 'react-redux';

// импортируем готовые пропсы из либы
import { NavLink, NavLinkProps } from 'react-router-dom';
import { TStore } from 'redux/store/store';

// примешиваем свои кастомные пропсы
interface NavLinkWithHashProps extends NavLinkProps {
title?: string;
}

// передаем в свой компонент-обертку все пропсы (существующие и кастомные)
const NavLinkWithHash: React.FC<NavLinkWithHashProps> = ({ title, ...props }) => {
const { hubId } = useSelector((state: TStore) => (
{ hubId: state.orders.filters.hubIds[0] }
));

const newHref = `${props.to}#ordersFilterHubId=${hubId};`

return (
<Tooltip title={title || ''}>
<NavLink {...props} to={newHref}>
{props.children}
</NavLink>
</Tooltip>
);
}

export default NavLinkWithHash;


Дженерики

Если есть повроряющиеся интерфейсы, то можно создать интерфейс с дженериком

interface LikeButtonProps1 {
className: string;
count: number;
size: number;
}

interface LikeButtonProps2 {
className: string;
count: number;
size: string;
}

interface LikeButtonProps3 {
className: string;
count: number;
size: boolean;
}

Создаем Дженерик

interface LikeButtonProps<T> {
className: string;
count: number;
size:T;
}

// используем
const a = (
obj1: LikeButtonProps<number>
obj2: LikeButtonProps<string>
obj3: LikeButtonProps<boolean>
) => {
return (
<ul>
<li>{obj1.size}</li>
<li>{obj2.size}</li>
<li>{obj3.size}</li>
</ul>
);
}

Partial

Может быть использован не весь тип данных, а только какая-то часть

type TProps = {
update: (pagination: Partial<IPagination>) => void;
params: Partial<IArrowParams>;
}