Search code examples
typescriptgenericsextendskeyof

How to verify that indexed type extends string?


let's say I have function func with 2 generic arguments

const func = <T extends {}, K extends keyof T>() => {};

and a type

interface Form {
  a: boolean;
  b: string;
}

then I can invoke them like so without any errors

func<Form, "a">();
func<Form, "b">();

Now I want func to accept only keys for which T[K] = string In other words

func<Form, "a">(); // should fail
func<Form, "b">(); // should pass

My pseduo-typescript solution would be

const func = <T extends {}, K extends keyof T : where T[K] extends string>() => {};

but that of course doesn't go far. Is it even possible? Any help is appreciated.


Solution

  • With a little helper type to get all the string types keys:

    type StringKeys<T> = {
      [K in keyof T]:
        T[K] extends string ? K : never
    }[keyof T]
    
    type Test = StringKeys<{ a: boolean, b: string, c: string }>
    // type: 'b' | 'c'
    

    This utility type maps over all property of T, and if the value type extends string the key name is preserved, and otherwise it is discarded as never.

    Then you simply use that like:

    interface Form {
      a: boolean;
      b: string;
    }
    
    const func = <T, K extends StringKeys<T>>() => {};
    
    func<Form, "a">(); // error
    func<Form, "b">(); // fine
    

    See Playground