Skip to main content

Typing effector

Best practices for writing well-typed code

createEvent#

By default, this method returns Event<void>.

const event = createEvent()
// event has type Event<void>
event()

Event type can be defined as generic

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

createEffect#

Typescript can infer effect result type from given handler, but argument type should be defined either in handler argument or as generic type

const sendMessageFx = createEffect(async (params: {text: string}) => {
// ...
return 'ok'
})
// sendMessageFx has type Effect<{text: string}, string>
const sendWarningFx = createEffect<{warn: string}, string>(async ({warn}) => {
// ...
return 'ok'
})
// sendWarningFx has type Effect<{warn: string}, string>

createEffect and custom errors#

When you need custom error types (Fail type in Effect) you can define all generics explicitly:

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

In case when effect's handler is defined before effect itself you can allow typescript to infer type of Params and Done by using typeof handler in first generic and optionally provide Fail type as second one

const sendMessage = async (params: {text: string}) => {
// ...
return 'ok'
}
const sendMessageFx = createEffect<typeof sendMessage, AxiosError>(sendMessage)
// sendMessageFx has type Effect<{text: string}, string, AxiosError>
since

effector 21.6.0

event.prepend#

To add types to events, created by event.prepend you need to add type either to prepend function argument or as generic type

const message = createEvent<string>()
const userMessage = message.prepend(({text}: {text: string}) => text)
// userMessage has type Event<{text: string}>
const warningMessage = message.prepend<{warn: string}>(({warn}) => warn)
// warningMessage has type Event<{warn: string}>

attach#

To allow typescript to infer types of created effect, add type to mapParams first argument, which will become effect params type

const sendTextFx = createEffect<{text: string}, 'ok'>()
const sendWarningFx = attach({
effect: sendTextFx,
mapParams: ({warn}: {warn: string}) => ({text: warn}),
})
// sendWarningFx has type Effect<{warn: string}, 'ok'>

split#

Typescript type predicates can be used to split common event type to several cases (hence the name)

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 has type Event<UserMessage>
// warnMessage has type Event<WarnMessage>

guard#

Typescript type predicates can be used to infer result type by filter function

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 has type Event<UserMessage>

createApi#

To allow typescript to infer types of created events, add type to second argument of given reducers

const $count = createStore(0)
const {add, sub} = createApi($count, {
add: (x, add: number) => x + add,
sub: (x, sub: number) => x - sub,
})
// add has type Event<number>
// sub has type Event<number>

is#

is methods can help to infer unit type (thereby is methods acts as TypeScript type guards) which can help to write strongly-typed helper functions

export function getUnitType(unit: unknown) {
if (is.event(unit)) {
// here unit has Event<any> type
return 'event'
}
if (is.effect(unit)) {
// here unit has Effect<any, any> type
return 'effect'
}
if (is.store(unit)) {
// here unit has Store<any> type
return 'store'
}
}
Last updated on