2022-06-15
|~2 min read
|242 words
I was working on a project recently where I wanted to check incoming data to see if it conformed to my allow list. To do this, I had an allow list of keys and would create a new object with only keys that matched the allow list, while tracking any others to see if they should be tossed.
Effectively, the code looked like:
type AllowList = "Foo" | "Bar"
const headers = {}
const disallowedKeys = []
for (const [key, value] of Object.entries(incoming)) {
switch (key) {
case "Foo":
case "Bar":
headers[key] = value
default:
disallowedKeys.push(key)
}
}
The question is, how could you ensure that headers
only receives keys of this type?
Two different ways - depending on whether you want the AllowList
to be string literals or an enum:
With an enum, it might look like:
enum ALLOWED_ENUM {
Authorization = "Authorization",
Another = "Another",
}
const headers: Partial<Record<ALLOWED_ENUM, string>> = {}
Whereas with string literals it might look like
type ALLOWED_STRINGS = "Authorization" | "Another"
const headers: Partial<Record<ALLOWED_STRINGS, string>> = {}
Since our headers
object is now typed, we get type checking on the rest of the function:
const disallowedKeys = []
for (const [key, value] of Object.entries(incoming)) {
switch (key) {
case "Authorization":
case "Another":
headers[key] = value
case "Not listed":
// headers[key] = value // -- this would error
default:
disallowedKeys.push(key)
}
}
Here’s a Typescript Playground demonstrating this.
Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!