Description
Bug Report
When typing a function parameter so that it's optional by typing it as void | SomeType
method and defining a default parameter, the type is not narrowed to SomeType
and instead stays as void | SomeType
. TS can be tricked into doing so by writing void | undefined | SomeType
, but this feels inconsistent.
From what I've read, void
means "I promise I won't use this return value" in the context of fnThatAccecptsVoidFn
in the code snippet below, so the return value can actually be anything, which means a default parameter in fnWithDefaultParam1/2
maybe cannot be used to narrow the function parameter to SomeType
.
This is either ok because wo broke our promise of not using the return value of the function in fnThatAccecptsVoidFn
- in this case void | SomeType
with a default value should also be narrowed to SomeType
. Or TS plays it safe and says "if void is accepted, anything could be passed in" and not narrow void | undefined | SomeType
to SomeType
.
Before you say "you could just make this an optional function parameter using ?
": This is the minimum reproducible, the actual code is in the context of sveltejs/svelte#7442 where we try to type out a function interface that makes it possible to either set a parameter as required, optional, or missing, where I found this (in my mind) inconsistency. This is related to #12400.
🔎 Search Terms
function default parameter void undefined
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about void, function default parameter
⏯ Playground Link
💻 Code
const fnWithoutDefaultParam = (param: void | {a: true}) => {
param.a; // fails, expected
}
const fnWithDefaultParam1 = (param: void | {a: true} = { a: true }) => {
param.a; // fails, unexpected?
}
const fnWithDefaultParam2 = (param: void | undefined | {a: true} = { a: true }) => {
param.a; // works, expected?
}
const fnThatAccecptsVoidFn = (fn: () => void) => {
fnWithDefaultParam2(fn()); // return param can be sth else than { a: true } or undefined. Type-hole that is ok because we break our promise of not using the return type?
}
fnThatAccecptsVoidFn(() => true);
🙁 Actual behavior
void | SomeType
+ a default parameter don't narrow it to SomeType
, but void | undefined | SomeType
do, which feels inconsistent. See first section for more details.
🙂 Expected behavior
Honestly I don't know, see sections above.