shrekshole
shrekshole
TTCTheo's Typesafe Cult
Created by shrekshole on 8/6/2023 in #questions
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
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
1 replies