Reputation: 16586
I have a function that takes a date format string (e.g., "YYYY-MM-DD"
or "YYYY"
) and returns a template literal type based on that format. The following code is a minimal example:
type ISODate = `${number}-${number}-${number}`;
type Year = `${number}`;
type ISOFormat = "YYYY-MM-DD";
type YearFormat = "YYYY";
function format(f: ISOFormat): ISODate;
function format(f: YearFormat): Year;
function format(f: ISOFormat | YearFormat) {
if (f === "YYYY-MM-DD") {
return "1234-12-12";
}
return "1234";
}
However, I get a compilation error (Typescript playground link):
This overload signature is not compatible with its implementation signature.
What I find really odd is the compilation error goes away when I change either of my return values to a template literal. For example (Typescript playground link):
type ISODate = `${number}-${number}-${number}`;
type Year = `${number}`;
type ISOFormat = "YYYY-MM-DD";
type YearFormat = "YYYY";
function format(f: ISOFormat): ISODate;
function format(f: YearFormat): Year;
function format(f: ISOFormat | YearFormat) {
if (f === "YYYY-MM-DD") {
return "1234-12-12";
}
return `${1}234`;
}
Can anyone help me understand why the former example has a compilation error but the latter compiles just fine?
Upvotes: 1
Views: 186
Reputation: 329963
The issue here seems to be that the compiler sees the call signature's return type as too wide for the implementation. The implementation signature is inferred to return "1234-12-12" | "1234"
. But one of the call signatures is claiming to be able to return ISODate
, a much wider type that does not completely subsume "1234-12-12" | "1234"
. Why the compiler cares about this is not clear to me. On the off chance that this is a bug in TypeScript, I've filed microsoft/TypeScript#44661. At the very least we should get word back that this isn't a bug and why.
UPDATE: According to this comment, the current behavior isn't a bug, but the intent was to prevent completely incorrect return types like "1234"
alone for ISODate
. They are taking the report as a suggestion to allow call signature return types that merely overlap with the implementation return type as opposed to being a supertype-or-subtype.
Anyway, my suggestion here is to explicitly annotate the return type of the function implementation to be the union of all the call signature return types. Then the compiler will see each call signature as compatible with it, while checking that the implementation is also compatible with the signature:
function format(f: ISOFormat): ISODate; // okay
function format(f: YearFormat): Year;
function format(f: ISOFormat | YearFormat): ISODate | Year {
if (f === "YYYY-MM-DD") {
return "1234-12-12";
}
return "1234";
}
Upvotes: 1