Eelilag
Eelilag

Reputation: 41

Using KeysOfType with generic

We have created the following type according to this post : https://stackoverflow.com/a/49752227

export type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp? P : never}[keyof T];

In this example

class A {
   public prop1: number;
   public prop2: string;
   public prop3: string;
}
class B<O extends A>
{
   private prop: KeysOfType<O, string>;
   private prop2: KeysOfType<A, string>;

   public method(): void
   {
      let o: O;
      let notTypedAsString = o[this.prop];
      let a: A;
      let typedAsString = a[this.prop2];
    }
}

The expression o[this.prop] is not typed as string despite the fact that this.prop is typed as a "string" property of O.

Link to the playground

Is there a way to make it work ?

Upvotes: 4

Views: 626

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249466

The answer you cite (always nice to be cited :) ) works well for usage site. So when you use the class or the function that uses KeysOfType you will get the expected behavior. The problem is that inside the class or function where the generic type is not known, typescript can't reason about what the indexing operation will produce.

We can use an approach similar to here. Where the generic parameter itself extends something that only has string keys (Record<K, string>). We will need an extra parameter K to capture only the string keys of the O type but it all works as expected:

export type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp ? P : never }[keyof T];

class A {
    public prop1: number;
    public prop2: string;
    public prop3: string;
}
class B<O extends Record<K, string> & A, K extends string | number | symbol = KeysOfType<O, string> >
{
    constructor() {

    }
    private prop: K;
    private prop2: keyof A

    public method(): void {
        let o!: O;
        let notTypedAsString = o[this.prop]; // if you hover over it it will be O[K] 
        notTypedAsString.bold();//but it behaves as string
        o.prop1 // number, A memebrs that arae not strings still work as expected
    }
}

new B<A>()
class A2 extends A {        
    public prop4: number;
    public prop5: string;
}
new B<A2>()

Upvotes: 1

Related Questions