Consider this scenario:
type THandlerArgsBase<TValue> = {
value: TValue | undefined;
}
type THandlerArgsUnnamed<TValue> = THandlerArgsBase<TValue> & {
name: undefined;
}
type THandlerArgsNamed<TValue> = THandlerArgsBase<TValue> & {
name: string;
}
type TProvaBase<TValue> = {
value?: TValue | undefined;
foo?: string;
blah?: number;
}
type TProvaUnnamed<TValue> = TProvaBase<TValue> & {
name?: undefined;
handler?: (args: THandlerArgsUnnamed<TValue>) => void;
}
type TProvaNamed<TValue> = TProvaBase<TValue> & {
name: string;
handler?: (args: THandlerArgsNamed<TValue>) => void;
}
type TProva<TValue> =
| TProvaUnnamed<TValue>
| TProvaNamed<TValue>
;
//generic implementation
function prova<TValue>(props: TProva<TValue>): void {
const {
value,
name,
handler,
} = props;
if (handler) {
typeof name === "string"
? handler({ name, value })
: handler({ name, value })
}
}
//usage
prova<number>({
name: "xyz",
handler: ({ name, value }) => void 0, //name is string: correct!
})
prova<number>({
handler: ({ name, value }) => void 0, //name is undefined: correct!
})
//specialization
type TProvaBool = Pick<TProva<boolean>, "value" | "name" | "handler">;
function prova_bool(props: TProvaBool): void {
const {
value,
name,
handler,
} = props;
if (handler) {
typeof name === "string"
? handler({ name, value }) //compilation error!
: handler({ name, value }) //compilation error!
}
}
In the latter method, all but one field are correctly inferred. The handler
field is unexpectedly inferred as:
handler: (args: never) => void
Interestingly, when I remove the Pick
operation to specialize the generic function, everything works fine:
type TProvaBool = TProva<boolean>;
I tested this in the Typescript playground (v5.0.4).
Why does inference break after using Pick
to specialize the generic function?
Is there a workaround for this issue?