I'm running into an issue when trying to infer generic parameters on generic types that are specifically objects:
type Box <T extends object> = { value: T; }
function set <T extends object>(box: Box <T> , newValue: T): void {
box.value = newValue;
}
const bbox: Box <{ foo: string }> = {
value: {
foo: "bar"
}
};
set(bbox, {
foo: "baz"
}); // OK as expected
set(bbox, 42); // ERR as expected
set(bbox, {
bar: "baz"
}); // ERR as expected
set(bbox, {}); // OK, unexpected
set(bbox, { /* no autocomplete/intellisense is given here for valid props */ });
If you provide {}
as the parameter, TypeScript infers {}
as a valid type for the function. This also means that in an editor, no autocomplete is given for the fields of the specified object type, since the {}
matches all objects.
Is there an order as to how TypeScript infers generic parameters (i.e. newValue
's inference overrides Box<T>
's inference, and any object is assignable to {}
)? If so, is there a way to avoid this?
You can partially solve this issue by adding an extra parameter:
function set <T, TN extends T> (box: Box <T> , newValue: TN): T {
box.value = newValue;
return newValue;
}
const bbox: Box <{ foo: string }> = {
value: {
foo: "bar"
}
};
set(bbox, {}); // ERR, expected
set(bbox, { /* still no autocomplete/intellisense */ });
However, you still do not receive any autocomplete/IntelliSense. I would guess it's because of the extends
, and how you're no longer looking for T
directly.
I don't know enough about how autocomplete works to tell you exactly why it doesn't work in your case; my intuition is that it hasn't fully committed to the generic type inference. One workaround that forces it to commit to the inferred type is to change set
into a curried function like this:
const intelliSet = <B extends Box<{}>>(box: B) =>
(newValue: B['value']) => set(box, newValue);
intelliSet(bbox)( { foo: 'bar' }); // note the second function call
intelliSet(bbox)( { /* autocomplete works here */ });
I don't know if there are non-workaround solutions. I'd be inclined to file a suggestion issue on GitHub, if one doesn't already exist. This one is possibly relevant, especially this comment. Anyway, good luck!