I'm going to ignore the empty interface issue whereby any non-nullish values are compatible with {}
. Add members to your interface and the () => example
calls will fail.
I don't think this is really documented anywhere obvious, but in TypeScript trailing function arguments that accept void
are considered optional, a feature implemented in microsoft/TypeScript#27522. So your two functions behave similarly.
declare let f1: (x?: string) => string;
declare let f2: (x: string | void) => string;
f1(); // okay
f2(); // okay
There's some weirdness surrounding this feature, such as the fact that it doesn't work in the face of generics (microsoft/TypeScript#29131 and microsoft/TypeScript#39600).
And the compiler doesn't see the types of f1
and f2
above as truly interchangeable, despite being callable in the same ways:
f1 = f2; // okay
f2 = f1; // error!
probably because void
is also a type given to return values from functions where you're not supposed to use the return value, leading to this bizarre situation:
f1(console.log()); // error
f2(console.log()); // okay?!
And the feature only applies to function parameters; object properties that accept void
are not considered optional (although it's been even suggested and implemented but not (yet?) merged in microsoft/TypeScript#40823).
So I'd say it's kind of a limited-scope not-fully-general not-fully-baked not-well-documented feature you've stumbled into. Oh well, hope that makes some sense. Good luck!
Playground link