Search code examples
typescripttypesinterfacekey

Is it possible to filter out keys from an interface / type?


For example, I have an interface:

interface Foo {
  name: string;
  id: number;
}

And I only want the keys of type string. I managed to change the type of id to never and get all the keys from Foo. But id is still a valid key, just with type never.

const bar: keyof Partial<{ [K in keyof Foo ]: Foo [K] extends string ? Foo [K] : never }> = 'id';

How do I get the keys from Foo of type string only?

What I want is:

const bar: FooStringKeys = 'name'; // ok
const bar: FooStringKeys = 'id;    // error

Solution

  • Adopted from the mapped types docs. TS Playground version here.

    interface Foo {
      name: string;
      id: number;
    }
    
    // Generic string property extractor
    type StringsOnly<Type> = {
        [Property in keyof Type as Extract<Property, Type [Property] extends string ? Type [Property] : never>]: Type[Property]
    };
    
    // Foo *string* Properties
    type FooString = StringsOnly<Foo>
    
    const fooString: FooString = {name: 'hello'}
    const notFooString: FooString = {id: 2} // error
    
    // Foo *string* Key names
    type FooStringKey = keyof FooString
    
    const fooStringKey: FooStringKey = "name"
    const notFooStringKey: FooStringKey = "id" // error