Let's talk about globals in TypeScript. Globals in TypeScript can be manipulated and used in really interesting ways.
Here we have a global reducer.
export const todosReducer: GlobalReducer<{ todos: { id: string }[] }> = (
state,
event
) => {
return state
}
And this global reducer has representation of state. We take in a state and have an event, and then we return the state, which is a typical pattern for a reducer.
Problem is, our event is typed as never. Let's fix that. We can access TypeScript's global scope using the declare global syntax. We'll use that then create a GlobalReducerEvent interface.
declare global { interface GlobalReducerEvent {} }
In it, we'll create some events
Let's say we have an add to-do event, where we can pass text with the string type.
declare global {
interface GlobalReducerEvent {
ADD_TODO: { text: string }
}
}
Now we can access that event in our global reducer.
export const todosReducer: GlobalReducer<{ todos: { id: string }[] }> = (
state,
event
) => {
// event.type = 'ADD_TODO'
return state
}
So we've also got another reducer called the user reducer, which is another global reducer. This reducer also now receives the add to-do event type since we used declare global.
If we want to do the same thing here, then we would just copy and paste the declare global code over. and we would add some more events to the global scope.
For instance, we might have a log in function and need to support a log-in event, which all reducers can receive. That's usually how it works in something like Redux.
declare global {
interface GlobalReducerEvent {
LOG_IN: { ... }
}
}
export const userReducer: GlobalReducer<{ id: string }> = (
state,
event
) => {
// event.type === 'LOG_IN'
// event.type === 'ADD_TODO'
return state
}
The way that this works is we have a types file. And in it, we declare an empty interface, GlobalReducerEvent. And then, there's a bit of a clever mapping type, which maps over all of the log in stuff, and then turns it into a union type.
declare global {
interface GlobalReducerEvent {
LOG_IN: {}
}
}
export type GlobalReducer<TState> = (
state: TState,
event: {
[EventType in keyof GlobalReducerEvent]: {
type: EventType
} & GlobalReducerEvent[EventType]
}[keyof GlobalReducerEvent]
) => TState
So here we're just saying EventType in keyof GlobalReducerEvent. And in that mapping, set the type to EventType. And then, we grab anything the user puts in their reducer events, and we append it to or intersect it with the event as well.
And this means that you can use globals. in really cool ways across your application.
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.