Created
June 25, 2020 15:10
-
-
Save steveadams/d716bfd2f966a194a1270f8aa93ee9d2 to your computer and use it in GitHub Desktop.
How to type disaptch maps in TypeScript?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is an attempt to understand how to solve this problem better. | |
// I have objects which I use as dispatch maps, or dispatch tables, however you like to call them. | |
// I want to type them so I'm certain I'm always using the right identifiers. Excuse the code, it's | |
// arbitrary but roughly describes how I'm using it. | |
const stringKeyedIcons: Record<string, Icon> = { | |
Facebook: () => { /* facebook icon */ }, | |
Google: () => { /* google icon */ }, | |
Twitter: () => { /* twitter icon */ }, | |
} | |
// Anything can go here, but I'll just use a void function | |
type Icon = () => void; | |
type IconName = keyof typeof stringKeyedIcons; | |
// Now when I want to reference icons, I can safely by using IconName: | |
const IconWrapper = (name: IconName) => { | |
// I know I can reference icons[name]! | |
} | |
// However, when I use IconWrapper I need to reference IconName using strings. | |
// My IDE offers suggestions and autocomplete, but this feels flimsy. | |
// For example, what if I rename a key of my object? | |
// const stringKeyedIcons: Record<string, Icon> = { | |
// Macebook: () => { /* renamed because facebook became medievil war reenactment social platform */ }, | |
// Noogle: () => { /* google merged with Nokia, took their N */ }, | |
// Zwitter: () => { /* Trying to stay hip I guess */ }, | |
// } | |
// My IDE has no idea, and I need to manually update all usages of IconWrapper: | |
// IconWrapper('Facebook') <-- This would be an error, and it would be scattered around my application | |
// Well, here's an alternative. I can use an enum which reflects my object's structure: | |
enum IconName { | |
Facebook, | |
Google, | |
} | |
const enumKeyedIcons: Record<IconName, Icon> = { | |
[IconName.Facebook]: () => { /* facebook icon */ }, | |
[IconName.Google]: () => { /* google icon */ }, | |
[IconName.Twitter]: () => { /* twitter icon */ }, | |
} | |
// This looks a little better in some ways, although it's fairly verbose. But I'm okay with that | |
// because I can rename my icons easily since I'll be using references to the enum. | |
// | |
// However, in cases where I need the enum value to match external sources, I need to use a | |
// non-numeric enum. For example, I've named notices that can be showed to a user, but I'm keeping | |
// track of those in localStorage so I don't show them more than once. I need detailed names for the | |
// notices so I don't get naming collisions, and it's clear how it's being used in the code: | |
enum NoticeID { | |
'NoticeTopic_2020_03_22' = 'NoticeTopic_2020_03_22', | |
'NoticeTopic_2020_04_13' = 'NoticeTopic_2020_04_13', | |
'NoticeTopic_2020_06_18' = 'NoticeTopic_2020_06_18' | |
} | |
const notices: Record<NoticeID, string> = { | |
[NoticeID.NoticeTopic_2020_03_22]: () => 'NoticeValue', | |
[NoticeID.NoticeTopic_2020_04_13]: () => 'NoticeValue', | |
[NoticeID.NoticeTopic_2020_06_18]: () => 'NoticeValue' | |
} | |
// Now when I trigger a notice in the application: | |
// actions.triggerNotice(Notices.NoticeTopic_2020_06_18); | |
// I can carry that NoticeID all the way through the dispatch and, once the notice is dismissed, | |
// mark that NoticeID as read in localStorage. | |
// | |
// The trouble is, this seems _really_ verbose and clumsy | |
// | |
// What I wonder is: What's a better way to do this? What am I missing in TypeScript that solves this better? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment