Search code examples
typescript

Using a map with template literal types throws error


I'm trying to follow the docs here.

Here's what I have:

enum InputFieldName {
    ReactionsCheckbox = 'reactionsEnabled',
    SettingsCheckbox = 'settingsEnabled',
}
type PropEventSource<Type> = `${string & keyof Type}Checkbox`;

const inputMap: Record<InputFieldName, PropEventSource<InputFieldName>> = {
    [InputFieldName.ReactionsCheckbox]: 'reactionsEnabledCheckbox',
    [InputFieldName.SettingsCheckbox]: 'settingsEnabledCheckbox',
}

const fieldName = inputMap[InputFieldName.ReactionsCheckbox]

console.log('fieldName', fieldName);

I correctly see reactionsEnabledCheckbox in the console, however I'm getting a typescript error:

Type of computed property's value is '"settingsEnabledCheckbox"',
  which is not assignable to type '"charAtCheckbox" | "charCodeAtCheckbox"
    | "concatCheckbox" | "indexOfCheckbox" | "lastIndexOfCheckbox"
        | "localeCompareCheckbox" | "matchCheckbox" | "replaceCheckbox" | ... 33 more ... | "valueOfCheckbox

How can I get this to work?

Also, prettier doesn't seem to see the following line as valid code:

type PropEventSource<Type> = `${string & keyof Type}Checkbox`;

Solution

  • Your enum InputFieldName has values of type string. That means that keyof InputFieldName will give you the keys of a string (="charAt" | "charCodeAt" | "concat" ...). Instead just use the actual enum type directly in your template literal type (`${string & Type}Checkbox`).

    Note that you can always use an enum in a template literal type (e. g. type Permutations = `${MyEnum}`;. You only need an intersection with string (= & string) because the generic parameter Type has no constraint.

    enum InputFieldName {
      ReactionsCheckbox = "reactionsEnabled",
      SettingsCheckbox = "settingsEnabled",
    }
    type PropEventSource<Type> = `${string & Type}Checkbox`;
    
    const inputMap: Record<InputFieldName, PropEventSource<InputFieldName>> = {
      [InputFieldName.ReactionsCheckbox]: "reactionsEnabledCheckbox",
      [InputFieldName.SettingsCheckbox]: "settingsEnabledCheckbox",
    };
    
    const fieldName = inputMap[InputFieldName.ReactionsCheckbox];
    

    TypeScript Playground