In TypeScript, a function is written as two overload signatures followed by one implementation signature. How many functions does the emitted JavaScript contain, and which of those signatures can a caller use?
answer
- three signatures, one emitted function
- headers carry no body
- implementation is not part of the public type
- erased — no runtime dispatch
- widening the body's signature adds nothing public
basics
~20 sThe emitted JavaScript contains exactly one function — overload signatures are types and are erased. Callers may use only the overload signatures; the implementation signature is invisible to them and cannot be called on its own terms.
solid answer
~40 sAn overload list is several bodyless headers written directly above one implementation that has the body. Only the headers form the function's public type, so a caller may use exactly those forms; the implementation signature exists purely so the compiler can type-check the body, and calling the function in a shape only that signature allows gives `No overload matches this call.` At runtime nothing survives: types are erased, so the output has a single function with no dispatch table and no argument-type checks. That is why the one body has to inspect what it actually received and behave accordingly. Overloads are worth the ceremony when the different call forms genuinely produce different result types or take different arities; when every form returns the same type, one signature with optional or union parameters is simpler.
go deeper
Be able to point at the three lines and say which is the implementation and which are the public forms, and state plainly that the emitted JavaScript has one function because types are erased.
Explain why a call matching only the implementation signature is rejected, and why widening that signature never widens the public API. Show a concrete call that errors and say what the compiler reports.
Show judgment about when an overload list is worth it at all — different arities or a result type that tracks the argument form — versus a single union or optional-parameter signature that is easier for callers and tooling to read.
Own the API-shape decision: overloads are a promise to callers you cannot later narrow without breaking them, and every extra form multiplies the cases the single body and its tests must cover. Weigh that surface against a simpler contract.
## The shape of an overload declaration TypeScript lets one function expose more than one public call signature. You write several **overload signatures** — headers ending in a semicolon, with no body — immediately above a single **implementation signature**, which is the one that carries the body: ```ts function makeDate(timestamp: number): Date; function makeDate(year: number, month: number, day: number): Date; function makeDate(a: number, b?: number, c?: number): Date { return b !== undefined && c !== undefined ? new Date(a, b, c) : new Date(a); } ``` The first two lines are the overload signatures. The third is the implementation. All of them must share the same name, and the headers must sit directly above the implementation with nothing in between. ## What actually gets emitted TypeScript's type layer is erased at compile time, and overload signatures are part of that layer. The JavaScript output contains **one** function called `makeDate`, built from the implementation signature's parameter list and body. There is no dispatch table, no per-signature entry point, and no generated runtime check of which form the caller used. In some other languages each overload is a genuinely separate routine selected by the compiler and present as its own member at runtime; in TypeScript there is only ever one function, and any behavioural difference between the forms has to be written by hand inside the single body. That erasure is also why overloads cost nothing at runtime — and why they guarantee nothing at runtime either. They are a description of how the function may be called, addressed entirely to the type checker. ## Which signatures a caller may use The overload signatures are the function's public type. The implementation signature is **not**: it is not added to the list of callable forms, and the compiler will not resolve a call against it. This routinely surprises people, because the implementation signature is usually the widest of the three: ```ts makeDate(1_700_000_000_000); // ok — matches the first overload makeDate(2026, 0, 1); // ok — matches the second overload makeDate(2026, 0); // error: No overload matches this call. ``` The two-argument call is fine as far as the implementation signature is concerned — `b` is optional and `c` is optional — but no *overload* accepts exactly two arguments, so it is rejected. The rule to remember: the implementation signature exists so the compiler can check the body, not so callers can call it. ## Why the implementation signature is wider The body must handle every form the overloads advertise, so its parameters are typically unions, and its trailing parameters are typically optional. Writing `a: number, b?: number, c?: number` is not a public promise that two arguments are allowed; it is the smallest signature under which all the advertised forms type-check inside one body. Widening it further never widens the public API — the only way to add a callable form is to add an overload signature. ## Where else the same form appears The same repeat-the-name-with-different-signatures pattern declares an overloaded method on a class or as a member of an interface. In an interface there is no implementation to write, so you simply list the member several times, and the same rule applies: those listed signatures are the whole public contract. ## When overloads are worth it An overload list earns its place when the call forms differ in a way one signature cannot express — most often when the **return type depends on which form was used**, or when different arities mean different things. If every form returns the same type and only the input varies, a single signature with a union parameter or optional parameters is shorter, easier to read, and avoids resolution surprises. ## Common mistakes - Assuming the implementation signature is "just another overload" and calling it. It is not part of the public type. - Expecting the runtime to pick a branch for you. Nothing was emitted to do that; the body must do it. - Duplicating the implementation signature as an overload header, which advertises a loose form you did not mean to support. - Reaching for overloads when a single, simpler signature already describes the function.
- If the implementation signature accepts two arguments, why is a two-argument call still an error?Because calls are resolved only against the overload signatures. The implementation signature is there so the compiler can type-check the single body against every advertised form; it is never added to the callable list. If you genuinely want the two-argument form, you must add an overload header for it.
- How would you declare the same overloaded operation on an interface, where there is no body to write?List the member more than once with different signatures. Because an interface has no implementation, the listed signatures are the entire contract, and a value assigned to that interface must be callable in all of the listed forms.
saying these in an interview costs you the question
- Thinks each overload signature emits its own JavaScript function
- Calls the function using the implementation signature's looser parameters
- Expects the runtime to dispatch on argument types automatically
- Believes overloads insert runtime type checks or validation
- Treats the implementation signature as a third public overload