Лучшие практики для написания хорошо типизированного кода

createEvent

По умолчанию этот метод возвращает Event<void>

const event = createEvent();
// => Event<void>
event();

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

const event = createEvent<number>();
// => Event<number>
event(0);

createEffect

TypeScript может вывести тип результата эффекта из заданного обработчика, но тип аргумента должен быть определен либо в аргументе обработчика, либо как дженерик

const sendMessageFx = createEffect(async (params: { text: string }) => {
  // ...
  return "ok";
});
// => Effect<{text: string}, string>

const sendWarningFx = createEffect<{ warn: string }, string>(async ({ warn }) => {
  // ...
  return "ok";
});
// => Effect<{warn: string}, string>

Типизация ошибок с createEffect

Некоторый код может выдать исключения только некоторых типов, например библиотека axios в качестве ошибок использует только AxiosError. В эффектах для описания типов ошибок используется дженерик Fail.

Для его указания, в случае, когда тип аргумента и тип результата задаётся явно (первым и вторым дженериком метода createEffect соответственно), используется третий дженерик метода:

const sendWarningFx = createEffect<{ warn: string }, string, AxiosError>(async ({ warn }) => {
  // ...
  return "ok";
});
// => Effect<{warn: string}, string, AxiosError>

В случае, когда обработчик эффекта определен до самого эффекта, TypeScript может определить тип Params и Done используя typeof handler в первом generic, не указывая сами типы явно. В таком случае описание типа ошибок можно передать в опциональный второй дженерик метода:

const sendMessage = async (params: { text: string }) => {
  // ...
  return "ok";
};

const sendMessageFx = createEffect<typeof sendMessage, AxiosError>(sendMessage);
// => Effect<{text: string}, string, AxiosError>
info

Fail в качестве второго дженерика добавлен в effector 21.6.0

event.prepend

Чтобы добавить типы к событиям, созданным с помощью event.prepend, необходимо добавить тип либо в аргумент функции prepend, либо как дженерик

const message = createEvent<string>();

const userMessage = message.prepend(({ text }: { text: string }) => text);
// userMessage имеет тип Event<{text: string}>

const warningMessage = message.prepend<{ warn: string }>(({ warn }) => warn);
// warningMessage имеет тип Event<{warn: string}>

attach

Чтобы позволить TypeScript выводить типы создаваемого эффекта, можно добавить тип к первому аргументу mapParams, который станет дженериком Params у результата

const sendTextFx = createEffect<{ text: string }, "ok">();

const sendWarningFx = attach({
  effect: sendTextFx,
  mapParams: ({ warn }: { warn: string }) => ({ text: warn }),
});
// sendWarningFx имеет тип Effect<{warn: string}, 'ok'>

split

TypeScript type predicates можно использовать для разделения исходного типа события на несколько вариантов (отсюда и название)

type UserMessage = { kind: "user"; text: string };
type WarnMessage = { kind: "warn"; warn: string };

const message = createEvent<UserMessage | WarnMessage>();

const { userMessage, warnMessage } = split(message, {
  userMessage: (msg): msg is UserMessage => msg.kind === "user",
  warnMessage: (msg): msg is WarnMessage => msg.kind === "warn",
});
// userMessage имеет тип Event<UserMessage>
// warnMessage имеет тип Event<WarnMessage>

guard

TypeScript type predicates можно использовать для вывода типа результата с помощью функции filter

type UserMessage = { kind: "user"; text: string };
type WarnMessage = { kind: "warn"; warn: string };

const message = createEvent<UserMessage | WarnMessage>();

const userMessage = guard(message, {
  filter: (msg): msg is UserMessage => msg.kind === "user",
});

// userMessage имеет тип Event<UserMessage>

createApi

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

const $count = createStore(0);

const { add, sub } = createApi($count, {
  add: (x, add: number) => x + add,
  sub: (x, sub: number) => x - sub,
});

// add имеет тип Event<number>
// sub имеет тип Event<number>

is

Методы группы is могут помочь вывести тип юнита, то есть они действуют как TypeScript type guards. Это применяется в написании типизированных утилит

export function getUnitType(unit: unknown) {
  if (is.event(unit)) {
    // здесь юнит имеет тип Event<any>
    return "event";
  }
  if (is.effect(unit)) {
    // здесь юнит имеет тип Effect<any, any>
    return "effect";
  }
  if (is.store(unit)) {
    // здесь юнит имеет тип Store<any>
    return "store";
  }
}
Перевод поддерживается сообществом

Документация на английском языке - самая актуальная, поскольку её пишет и обновляет команда effector. Перевод документации на другие языки осуществляется сообществом по мере наличия сил и желания.

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

Соавторы