0

Need to tips for make generic. Want to make Child class property keys as argument for Super.method and, Child[key] should be a Sub class.

class Parent {
  method<T extends keyof this>(keys: T[]){
  }
}

class Child extends Parent {
  a = new Sub;
  b = new Sub;
  c = new Sub;
  d = new Nope;
  e = new Nope;
}

child = new Child;
child.method(['a', 'b', 'c']);

just displayed all property keys. does not known next for filtering of class.

assistant displayed keys like below.

  • 'a'
  • 'b'
  • 'c'
  • 'd'
  • 'e'

want to change like below:

  • 'a'
  • 'b'
  • 'c'

2 Answers 2

1

Yes, you can get this to happen automatically.

Caveat: You didn't give a type definition for Sub or Nope, so I will add some. It's actually important to do so in the example, because the TypeScript type system is based on stucture, not names. Meaning that the mere fact that the names Sub and Nope are distinct doesn't mean that the compiler sees them as distinct types. If they have the same properties, the compiler will see them as the same type, and then the task of distinguishing class properties of type Sub from those of type Nope would be impossible. So let's do this:

// important that Sub and Nope are stucturally distinct types
// so the compiler can tell the difference
class Sub {
  sub!: string;
}
class Nope {
  nope!: string;
}

Now Sub has a "sub" key and Nope has a "nope" key, and the compiler will be able to tell them apart.

You can create the following type alias KeysMatching<T, V>, which will produce all keys of T where the property at that key is assignable to the type V:

type KeysMatching<T, V> = {[K in keyof T]: T[K] extends V ? K : never}[keyof T];

It makes use of mapped and conditional types. Now your method can be typed the way you want by replacing keyof this with KeysMatching<this, Sub>:

class Parent {
  method<T extends KeysMatching<this, Sub>>(keys: T[]){
  }
}

class Child extends Parent {
  a = new Sub;
  b = new Sub;
  c = new Sub;
  d = new Nope;
  e = new Nope;
}

Let's make sure it works:

const child = new Child;
child.method(['a', 'b', 'c']); // okay
child.method(['d','e']); // error!

Looks good to me. Here's a Playground link to this code. Hope that helps; good luck!

Sign up to request clarification or add additional context in comments.

Comments

0

You can remove d and e from being valid selections by either removing them from your class or making them private. The auto-completion list will then only feature 'a', 'b', 'c', 'method' - note that method is there because it is inherited from the base class.

You could also take more control by eliminating all but your specified names, though this would be tedious to maintain:

class Parent {
    protected baseMethod<T extends keyof this>(keys: T[]){
    }
}

class Child extends Parent {
    a = new Sub();
    b = new Sub();
    c = new Sub();
    d = new Nope();
    e = new Nope();

    method<T extends keyof this & 'a' | 'b' | 'c'>(keys: T[]) {
        this.baseMethod(keys);
    }
}

In the above example, attempting to specify d or e will result in a warning.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.