I’m implementing a type-safe shapeAdapter
function in TypeScript that takes an original object, an adapt
function to transform it, and a revert
function to convert it back.
function shapeAdapter<Original, Transformed>(props: {
original: Original;
adapt: (original: NoInfer<Original>) => Transformed;
revert: (transformed: NoInfer<Transformed>) => NoInfer<Original>;
}) {
throw new Error("Implementation does not matter")
}
The function works as expected when adapt
is declared before revert
:
shapeAdapter({
original: { value: 1 },
adapt: (original) => original.value,
revert: (transformed) => ({ value: transformed }), // transformed is of type number as expected
})
However, when I swap the order of adapt
and revert
, the type inference breaks:
shapeAdapter({
original: { value: 1 },
revert: (transformed) => ({ value: transformed }), // "transformed" is of type unknown! should be number
adapt: (original) => original.value,
})
In the second example, transformed
in the revert
function is inferred as unknown
instead of number
.
I’m using the NoInfer
utility type to prevent excessive type widening, but it doesn’t solve this issue.
Is there a way to maintain correct type inference regardless of the order of these callbacks?
TypeScript Version: 5.5.2
Playground Link TS Playground
What I tried:
I initially implemented the function with adapt
before revert
, which worked as expected, but then i attempted to reorder the arguments, placing revert
before adapt
, but it broke the type inference, then i tried to use the NoInfer
utility type on the generic parameters to prevent excessive type widening, but that didn’t help either.
What I expected:
I expected TypeScript to be able to infer the relationship between the Original
and Transformed
types based on the structure of the shapeAdapter
function, regardless of the order of the callbacks.