I want to manage unique keys being auto-completed in TypeScript
投稿日: 2024/06/25
更新日: 2024/07/01
This article is a translation of the following article.
While doing front-end development, we need to manage unique keys in WebStorage (LocalStorage, SessionStorage), Recoil and React Query (TanStack Query).
I came up with a simple method for managing keys, so let me introduce it.
What you will learn in this article
- Simple management method for unique keys
- You do not need to enter almost the same string twice every time you add a key like enums.
- Auto-completion works based on type information.
- Duplicate keys will result in a type error.
If you want to manage more Keys efficiently, we recommend the following articles that enable hierarchical management.
I made it with the motivation that it doesn't have to be so particular for my current use case and that a simpler code would be good. In this article, hierarchical key management is not possible, but the goal is to achieve efficient key management with a short code anyway.
I propose "Technique for automatically generating values for associative arrays".
Before introducing my code, I will introduce a method using Enum as a comparison management method.
Management method using 'enum'
The method using enums is introduced in the referenced blog as follows.
It is very simple and completes well, but it's a bit cumbersome that not only the key but also the value needs to be specified by a human every time (such monotonous work is prone to mistakes, so I would like the value side to be unnecessary to be specified).
It may be a bit cumbersome, but it is a sufficiently practical method.
RecoilKeys.ts
export enum RecoilAtomKeys { TODO_STATE = 'todoState', NOTICE_STATE = 'noticeState' } export enum RecoilSelectorKeys { TODO_TODOS = 'Todo_todos', TODO_TODO_ITEM = 'Todo_todoItem', NOTICE_HAS_UNREAD_NOTICE = 'Notice_hasUnreadNotice' }
This is a digression, but when using this method, it is more efficient to rewrite the implementation using Union Types after reading Reasons why it is better not to use TypeScript's enum from the perspective of Tree-shaking.
Technique for automatically generating values for associative arrays
This is the method we are proposing this time.
Here is the code.
frontend/helper/webStorage/localStorage.ts
const keysObject = { csrfToken: "", authToken: "", } as const type Key = keyof typeof keysObject const keys = Object.keys(keysObject) as Key[] export const localStorageKeys = keys.reduce( (obj, key) => ({ ...obj, [key]: key }), {} as { [key in Key]: string } )
By using the variable localStorageKeys
created like this, completion from type information as shown in the image below is convenient. Also, when adding keys to the variable keysObject
, if there are any duplicates, a type error will be displayed, preventing accidents.
One unfortunate point is that in order to detect duplicates as type errors, it was necessary to use an associative array, which requires some value to be set in the Value field (even if it is not used!).
I will explain the code step by step.
Store an associative array in the variable keysObject
with unique Keys you want to use. Put an empty string as the Value in the associative array since it is not being used (null
, undefined
, or anything is fine).
The type Key = keyof typeof keysObject
retrieves a list of keys for the variable keysObject
as a Union type. Here, it looks like this:
type Key = "csrfToken" | "authToken"
With const keys = Object.keys(keysObject) as Key[]
, we are actually extracting the list of Keys from the variable keysObject
as an array. keys === ['csrfToken', 'authToken']
will be the result.
(At this time, the type becomes string[]
, so we are using as
to convert it to the Key[]
type. The type of the assigned variable keys
becomes ("csrfToken" | "authToken")[]
.)
Lastly, I would like to discuss the processing of the following code.
export const localStorageKeys = keys.reduce( (obj, key) => ({ ...obj, [key]: key }), {} as { [key in Key]: string } )
I will generate an associative array using the variable keys
, where the Key and Value are the same string.
localStorageKeys === { "csrfToken": "csrfToken", "authToken": "authToken" }
.
Summary and Side Notes
At first, I was managing the list of keys as an array.
以下のコードはおすすめしない
// このコードは非推奨 const keys = [ "csrfToken", "authToken", ] as const type Key = typeof keys[number] export const localStorageKeys = keys.reduce( (obj, key) => ({ ...obj, [key]: key }), {} as { [key in Key]: string } )
When adding a Key, the description can be kept to a minimum, making it difficult to notice duplicated values in the array. Especially since duplicated Keys are automatically grouped together without any errors, I believe it is dangerous.
I tried various ways to generate a type error successfully, but in the end, I couldn't come up with a good idea. If you have any good ideas, please leave a comment.
If it's a runtime error, it can be easily displayed, but it's tricky (not to say it doesn't exist).
So, it may be a bit redundant, but we decided to adopt an associative array.