What you are seeing is just a consequence of how union types work. Union (unless narrowed) only allow access to common properties. So when you say keyof X
the only properties that will be present in the resulting string literal union are x
(which is no dependent on the union) and type
which is common to both union members. Pick
also uses keyof
behind the scenes and so has the same issue, it will be able to pick any members of the union that are common to all union members.
You can get the desired behavior, but you can't use keyof
and Pick
directly. You need to use the in a conditional type with a naked type parameter. Conditional types will distribute over members of a union (you can read more here about this behavior or a more concise version here) allowing us to apply keyof
and Pick
over each member of the union instead of the union as a whole.
type A =
| {
type: 'a';
a: string;
}
| {
type: 'b';
b: string;
};
type X = {
x: string;
} & A;
type UnionKeys<T> = T extends any ? keyof T : never;
type UnionPick<T, K extends UnionKeys<T>> = T extends any ? Pick<T, Extract<K, keyof T>> : never
type XX = UnionPick<X, Exclude<UnionKeys<X>, 'x'>> & {
x: number;
};
const x: XX = {} as any;
if (x.type === 'a') {
console.log(x.a);
}