Nested Generic Inference

Got a question on the correct way to type generics when you've got one generic inside another.

A few examples of implementations. I'm mainly confused why example 1 isn't able to infer the type of inner correctly.
Is there a correct way of doing this?

type ConcreteInnerType = {
  id: string;
  someInnerKey: string;
}

type ConcreteOuterType = {
  inner: ConcreteInnerType
  someOuterKey: string
};

const myConcreteOuter: ConcreteOuterType = {
  inner: {
    id: 'myId',
    someInnerKey: 'innerValue'
  },
  someOuterKey: 'outerValue'
};



type GenericOuter<T> = { inner: T };
type GenericInner = { id: string }
// Example 1 - Nested generics
const getInnerAndOuter1 = <Outer extends GenericOuter<Inner>, Inner extends GenericInner>(
  outer: Outer
) => ({
  inner: outer.inner,
  outer
});
const result1 = getInnerAndOuter1(myConcreteOuter);
// ❌ Why is it unable to infer the concrete type of 'inner' here?
result1.inner.someInnerKey
// ✔️ This is fine
result1.outer.inner.someInnerKey



// Example 2 - Only using Inner generic
const getInnerAndOuter2 = <Inner extends GenericInner>(
  outer: { inner: Inner }
) => ({
  inner: outer.inner,
  outer
});
const result2 = getInnerAndOuter2(myConcreteOuter);
// ✔️ This now works
result2.inner.someInnerKey
// ❌ This now doesn't
result2.outer.someOuterKey



// Example 3 - Explicit return type
const getInnerAndOuter3 = <Outer extends GenericOuter<Inner>, Inner extends GenericInner>(
  outer: Outer
): { inner: Outer["inner"], outer: Outer} => ({
  inner: outer.inner,
  outer
});
const result3 = getInnerAndOuter3(myConcreteOuter);
// ✔️ This now works
result3.inner.someInnerKey
// ✔️ And so does this
result3.outer.someOuterKey
Was this page helpful?