How to append a long list of static properties to a function in Typescript

Viewed 271

I'm trying to type this function that has a long list of static strings appended to it as properties which return the property as a string value:

const arr = ["a", "b", "c"]; // actual list has about 140 items

const f = (tag: string | undefined) => tag;

arr.forEach(key=> {
  f[key] = f(key)
})

console.log(f.a) // "a"
console.log(f.b) // "b"
console.log(f.c) // "c"
console.log(f.d) // undefined

Errors:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '(tag: string) => string'. No index signature with a parameter of type 'string' was found on type '(tag: string) => string'.
Property 'a' does not exist on type '(tag: string) => string'.
Property 'b' does not exist on type '(tag: string) => string'.
Property 'c' does not exist on type '(tag: string) => string'.
Property 'd' does not exist on type '(tag: string) => string'.

Typescript Playgound


1 Answers

First, you'll need to declare the array of static properties as const to make the type ['a', 'b', 'c'] instead of string[].

const arr = ["a", "b", "c"] as const;

Now get those string constants out as a union of string literals by getting the type of that that array, and then indexing that type by number.

type Keys = (typeof arr)[number] // "a" | "b" | "c"

Then declare the type of the function, and the static properties separately.

type TagFn = (tag: string | undefined) => string | undefined
type StaticProps = { [key in Keys]: string }

Now you can intersect those types to make the static properties part of the function type.

type TagFnWithStatic = TagFn & StaticProps

Typescript won't like it if you create a function and say it's a TagFnWithStatic type without the static properties declared, because those properties must be defined to satisfy the type. To fix that, let's generate the static properties as their own object separately.

const staticProps = arr.reduce((result, prop) => {
  result[prop] = prop;
  return result
}, {} as StaticProps)

Which can then be merged with the function on assignment to a variable via Object.assign()

const f: TagFnWithStatic = Object.assign(
  (tag: string | undefined) => tag,
  staticProps
)

Now the following should work as you expect:

f.a // type: string
f.b // type: string
f.c // type: string

f.d // type error, because static property is not present in `arr`

Playground

Related