Problem

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" });

Solution 1

이럴때는 그냥 분리하지말고, 유니온을 그대로 사용하면 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" } });

Solution 2

분리를 하고싶다면, 타입 캐스팅과 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" });

0개의 댓글