Search code examples
typescripttypescript-typingsunion-types

Union type of "keyof" and "string" with working autocompletion


I want to expose a method that takes some Union type as parameter and provides autocompletion for that. As this union type could be extended via declaration merging, autocompletion is a key feature here.

But in cases where the user doesn't use TypeScript when using my library, or does not want to add particular typings to the declaration, it should alternatively accept a string alternatively to the union type.

In short: I want autocompletion for the union type, but also allow any string that does not match it.

Example with only the union type:

export interface Values {
   'variant-a': undefined,
   'variant-b': undefined,
   'variant-c': undefined,
}

function doAThing<T extends keyof Values>(name: T): void {
  console.log(name);
}

Things I tried:

  1. Adding a function overload: function doAThing<T extends keyof Values>(name: string): void; - does merge types with keyof Values, and becomes string for both then. No autocompletion.
  2. Make the generic type a union with string: <T extends keyof Values | string>- merges to only string. No autocompletion as well.

Solution

  • I found a solution here:

    export type Values = {
       'variant-a': undefined,
       'variant-b': undefined,
       'variant-c': undefined,
    }
    
    type AnyString<T> = T | (string & {});
    
    function doAThing<T extends keyof Values>(name: AnyString<T>): void {
      console.log(name);
    }
    
    doAThing('variant-b');  // Autocompleted
    doAThing('variant-d');  // Not autocompleted, but still accepted