"오버로드는 한 구현이 정말 여러 얼굴로 호출될 때 쓰는 도구야. 단지 입력 타입이 둘이라는 이유만으로 꺼내진 마."
공개 시그니처와 구현 시그니처
함수 오버로드는 먼저 호출자가 볼 시그니처를 여러 개 적고, 마지막에 그 모든 경우를 처리하는 구현 하나를 둬. 호출자는 공개된 오버로드만 사용할 수 있고 구현 시그니처는 직접 보지 못해. 구현이 넓게 unknown이나 유니언을 받더라도 허용되는 호출이 자동으로 늘지는 않아.
구현은 모든 오버로드 입력을 받아 처리할 수 있어야 하고, 각 분기에서 약속한 반환값을 만들어야 해. 공개 계약과 실제 분기 사이가 어긋나면 타입 표면은 그럴듯하지만 실행에서 틀린 결과가 나올 수 있으니 테스트도 필요해.
유니언과 제네릭을 먼저 검토해
입력 타입만 여러 개이고 반환 타입이 같다면 value: number | Date 같은 유니언 하나가 더 간단할 때가 많아. 입력 타입과 반환 타입의 관계를 그대로 보존하려면 제네릭이 잘 맞아. wrap<T>(x: T): Box<T>는 가능한 타입마다 오버로드를 늘릴 필요가 없어.
오버로드가 빛나는 자리는 호출 모양 자체가 다를 때야. 인자 개수가 다르거나, 특정 입력 모양에 따라 반환 모양이 뚜렷이 달라지고 한 시그니처로 표현하면 호출자가 쓰기 어려워지는 경우지. 편집기의 자동 완성에 각 호출 형태를 별도 문서처럼 보여 주고 싶을 때도 가치가 있어.
유니언으로 충분하면 유니언, 관계를 보존하면 제네릭, 정말 다른 호출 계약만 오버로드. 이 순서가 API를 짧고 확장 가능하게 지켜 줘.
피파의 고백
오버로드를 쓰면 타입을 정교하게 만든 기분이 들어. 그래서 과하게 쓰기 쉬워. 나중에 새 경우 하나를 추가하며 시그니처 다섯 개를 고치게 되면, 사실 원한 것은 제네릭 하나였다는 걸 자주 깨닫지.