Union 타입을 분리하여 함수에서 사용할 경우, 파라메터 추론이 정상적으로 동작하지 않는다.
type Union =
| {
type: "A";
value: {
foo: 1;
};
}
| {
type: "B";
value: {
bar: string;
};
}
| {
type: "C";
value: {
baz: Record<string, string>;
};
};
type ExtractParams<T extends Union["type"], U extends Union = Union> = U extends { type: T } ? U["value"] : never;
function inferFn<T extends Union = Union>(type: T["type"], data: ExtractParams<T["type"]>) {
if (type === "A") {
// parameters - error
data.foo;
}
if (type === "B") {
// parameters - error
data.bar;
}
if (type === "C") {
// parameters - error
data.baz;
}
}
// arguments - pass
inferFn("B", { bar: "3" });
이럴때는 그냥 분리하지말고, 유니온을 그대로 사용하면 Exhaustive check 에 의해서
아규먼트, 파라메터 둘 다 정상적으로 추론된다. (단 디스트럭쳐링 등을 사용하여 유니온 데이터 원형으로부터 분리되는 순간 깨진다.)
function inferFn(data: Union) {
if (data.type === "A") {
// parameters - pass
data.value.foo;
}
if (data.type === "B") {
// parameters - pass
data.value.bar;
}
if (data.type === "C") {
// parameters - pass
data.value.baz;
}
}
// arguments - pass
inferFn({ type: "B", value: { bar: "3" } });
분리를 하고싶다면, 타입 캐스팅과 Exhaustive check 을 섞는 방법도 있다.
function inferFn<T extends Union = Union>(type: T["type"], data: ExtractParams<T["type"]>) {
const inferredData = { type, value: data } as Union;
if (inferredData.type === "A") {
// parameters - pass
inferredData.value.foo;
}
if (inferredData.type === "B") {
// parameters - pass
inferredData.value.bar;
}
if (inferredData.type === "C") {
// parameters - pass
inferredData.value.baz;
}
}
// arguments - pass
inferFn("B", { bar: "3" });