jpaddison3
jpaddison3

Reputation: 1996

Infer nested generic types in typescript?

Suppose I have

interface Animal {}

interface Group<A extends Animal> {}

And I want to make an interface generic over Group

interface AnimalGroupProps<G extends Group<A>, A extends Animal> {
  withGroup: () => G
  // We want to be able to reference A in the interface, for use like this:
  withLeader: () => A
}

I want animal group props to be generic over the group type. But the A extends Animal seems redundant. I'd like to be able to say:

interface AnimalGroupProps<G extends Group<A>>

And have TypeScript figure it out. But TypeScript wants A to be declared, so I have to use the previous snippet's pattern.

class Wolf implements Animal {}

class WolfPack implements Group<Wolf> {}

function AnimalPlanetWolves ({withGroup, withLeader}: AnimalGroupProps<WolfPack, Wolf>) {}
//                          This is the really annoying part --------------------^^^^

All users of AnimalGroupProps have to specify both generic parameters, even though one of them is entirely redundant. In my actual codebase, that would be a lot of redundancy.


In the above example WolfPack was not a generic type. What if there was a generic type that you wanted to pass to AnimalGroupProps? It's actually even worse:

interface Flock<A extends Animal> extends Group<A> {}

class Geese implements Animal {}

function AnimalPlanetBirds ({withGroup, withLeader}: AnimalGroupProps<Flock<Geese>, Geese>) {}

Upvotes: 4

Views: 2836

Answers (1)

ccarton
ccarton

Reputation: 3666

Yes, Typescript has a syntax to infer nested types.

type AnimalForGroup<G> = G extends Group<infer A> ? A : never

You still need to list A in the template arguments but you can give it a default value:

interface AnimalGroupProps<G extends Group<A>, A extends Animal = AnimalForGroup<G>> 

Upvotes: 13

Related Questions