Search code examples
typescriptgenericstypesinterface

Using generics for classes with and without an array: Type 'string' does not satisfy the constraint 'string[]'


I have a simple interface:

interface Animal<T> {
  isMatch: (value: T) => boolean
}

with these implementations:

class Dog implements Animal<string> {
  isMatch(input: string): boolean {
    // do something with string
  }
}

and

class Cat<T extends T[]> implements Animal<T> {
  isMatch(input: T[]): boolean {
    // do something with list
  }
}

I can instantiate a Dog without issue with:

const dog = new Dog("pug")

However, I can't instantiate a Cat with any of:

const siamese = new Cat<string>(...) // Type 'string' does not satisfy the constraint 'string[]'

const persian = new Cat<string[]>(...) // Type 'string[]' does not satisfy the constraint 'string[][]'

const sphynx = new Cat(["birman"]) // Type 'string' does not satisfy the constraint 'string[]'

I've used the generics / extends based on examples I've seen online, so not sure what I'm misunderstanding here.


Solution

  • I understand why you used T extends T[], because of this error when you don't:

    Type 'T' is not assignable to type 'T[]'.(2416)

    input.tsx(11, 11): This type parameter might need an extends T[] constraint.

    This error is misleading in this case, but if we think through what we want, we can get the solution.

    You want isMatch to take the type T[]. However, Cat only takes T. Remember that isMatch is defined by the interface Animal, and in the interface, it takes T (in the interface). If we want these types to match, we just have to make Animal take T[]!

    class Cat<T> implements Animal<T[]> {
    

    And that's it.

    Playground