I am currently developing a function that receives a descriptor object and leverages the inferred type information to generate another object with a user-friendly API and strong typing.
One issue I have encountered is that TypeScript only infers the types of nested properties if they are restricted to something like a union type. If they are constrained to a primitive type (e.g., string
), they retain that primitive type and do not narrow down to the specific literal value passed in.
type ChildType = 'foo' | 'bar' | 'baz';
type ChildDescriptor<
TName extends string = string,
TType extends ChildType = ChildType,
> = {
name: TName;
type: TType;
};
type ParentDescriptor<
TSomeProp extends string,
TChildren extends ChildDescriptor[],
> = {
someProp: TSomeProp;
children: [...TChildren];
};
// Identity function used to observe inferred types
const identity = <
TSomeProp extends string,
TChildren extends ChildDescriptor[],
>(
descriptor: ParentDescriptor<TSomeProp, TChildren>,
) => descriptor;
const inferredValue = identity({
someProp: 'I get inferred',
children: [
{
name: 'I don\'t get inferred',
type: 'foo',
},
{
name: 'I don\'t get inferred either',
type: 'baz',
}
],
});
const [childA, childB] = inferredValue.children;
The example above demonstrates how the children
property of inferredValue
is correctly inferred as a tuple, and the someProp
property as well as the type
property of the children objects are narrowed down to the particular literal types as intended. However, the name
property of the child objects remains typed as string
, and I'm uncertain as to why.
I can circumvent this by applying as const
after each non-narrowing value, or even add it after each child object. However, this workaround does not extend beyond a certain level due to the tuple becoming read-only, which complicates working with generics in an API context. Ideally, I would prefer to avoid this solution altogether as using a type assertion feels more like a temporary fix than a permanent resolution.
In the end, these descriptor objects will be auto-generated through another software tool that I am developing, so I may resort to the workaround if necessary. Nonetheless, I wanted to explore potential alternative solutions that may elude me at this moment.
The project I am involved in operates within the confines of TypeScript 4.7.4, limiting me to utilizing features available in that particular version.