microsoft / microsoft/TypeScript
Omitted arguments are not candidates for possibly-undefined type parameters
@gabritto is already working on this.
Since Jan 31, 2025.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
# Background
[Playground Link](https://www.typescriptlang.org/play/?isolatedDeclarations=false&ts=5.8.0-dev.20250128#code/C4TwDgpgBAigrgSwMYGsAKyUCULDgJwDsAeAWTgBtgEwLoIAPYCQgEwGcoAjAex7oCGhKAB8ocNhABmCQhFYA+KAF4AUFA1RyVGnSiNmbTsHxxoAfijsTsgOYBtALpQAXOs3bqtekxYcoUgIU7NBiEqzSsvJQltb4dq7uGnIAbhD4ANyqqgD0AFR56nlQAAJgAvgCALZQYPg8VWDAUAAqABbQzExQwDxWbTwA7j19AuIh+AB0RaXllTUIzFWcAOodwB34UGNwE1BIQlYQdEjNVZReejxNCDyE7AA0UDxbAFa7zWPsdlc3d9NQYplCrVfZCADKxwgp08umgAFEBEg2s8pD0Os8-vdavgICFCMxWCN0dBdukAQUcqopBJTrdhOwBoN4JgMKgyBc4fpfEZuHxBMIwpIZHJFAAKJI4hpNFxWGyEWwPSWLCDLWW4gSsO4UEBy+IKpxKzRgwiQk7AWHecyyy10JUASllLNQbOwuAIJFtECUAG9JXRmlw4MBetjlFAVctJlUBGAxWLI09ZBEGPaVEoxX7jcaQub5LLk4wVMpwwAGI3ZjRdYAFpYVjQAX3t9qykpyOWeG3S+x4ESgth4eJ7fc2EEm4+yxoQaLFB1NUJhnO8aazlaguLwRG4wdD7Ela6gkxkVHSYqDIbu6e3F8Ik1z0MJ9v3a+jsbPO8vyiU59Dk2rLclBtJQ3D1r13I9k3fG8rx-f571OeR7QAQj-AwsiA3IOwAAxMMwsP7BA0k4MYsLiOwnHwgBaKBBheFBOEAUHJVADfprjAOwABkEGsWUyINZxw0ZIZnXQTAJWNAAiNZkBRKRTEWTgtSgEAeDgGihGaXpagIZEBBCcwJPrKB7Ak2NvHYQyoAkl4hFsPFLIkrghGciyngk1gCAQIQJMcIzcIgB1WyQO52H4McKB4WwxSwgBBCh4lsNpgCeAcElUzS+iDXVQqqaAABIfSEsB2IVLjrEmV4eFkMUHPtBssIAzCoCw8JIlFfDbEIocSL42wqJoujGOY3AoFYWQ5HwABGXj5VsFRWOZRAXTEyUpLaARgAAck4KQXlG8bu1DBBEuAAyjJM9hdjaBAHPKawBAc4AkR4VyrLgRKoAASU2mpej6NoJFsfBdS0jZZBQJ5BnW5g0i2FS1MGDSfKMwJggC1RGvbZrWpFeQOq64jmt6-raPweioCYlixsICaACYZv1ObBKZETXXEzQ1o27aAj26mJpGQhjqSs7JQuq6brcu6nse57Xok96UW+36+CgAGFWB4kwcICGaOhiBYeU1T1IJZHJRxqJWEC7IsYAeSqRZCW2fBbDgXKCQIojtiJ2bRD1cjHH6wAZcmG5o+fSABmBmEmZ4SltE1B2Y0Tmtp23mDq2I6TpF40xcZCWrKlh63Ke4K5YVr6fpGf7AY10Hru1yG9YN+HjeAU2WyAA)
Consider the following code that is trying to leverage #56941.
```ts
type QuickPickReturn =
Multiple extends true ? string[] :
Multiple extends false | undefined ? string :
never;
/**
* @param prompt The text to show to a user.
* @param items Whether a user can select multiple options, or just a single option.
* @param canSelectMultiple Each of the options presented to the user.
**/
function showQuickPick(
prompt: string,
items: readonly string[],
canSelectMultiple?: Multiple,
): QuickPickReturn {
let buttons = items.map((item, index) => ({
selected: index === 0,
text: item,
}));
// other code goes code here...
if (canSelectMultiple) {
return buttons
.filter(button => button.selected)
.map(button => button.text);
}
return buttons.find(button => button.selected)!.text;
}
```
This code has an optional parameter `canSelectMultiple`. Because it is optional, the `Multiple` type parameters cannot simply be constrained to `boolean` - they must factor in `undefined` for various narrowing and assignability checks to work out.
Most calls work as expected.
```ts
// `true` gives a `string[]` - works ✅
let shoppingList: string[] = showQuickPick(
"Which fruits do you want to purchase?",
["apples", "oranges", "bananas", "durian"],
true,
);
// `false` - works ✅
let dinner: string = showQuickPick(
"What's for dinner tonight?",
["sushi", "pasta", "tacos", "ugh I'm too hungry to think, whatever you want"],
false,
);
```
`undefined` explicitly also works
```ts
// `undefined` gives a `string` - works ✅
let dinner2: string = showQuickPick(
"What's for dinner tonight?",
["sushi", "pasta", "tacos", "ugh I'm too hungry to think, whatever you want"],
undefined,
);
```
However, if we omit the argument, we assume that there are no candidates for inference available and fall back to the constraint `boolean | undefined`. This changes our output type to `string | string[]`, which is an error in the following code:
```ts
// Omitted argument gives a `string | string[]` - ❌
let dinner3: string = showQuickPick(
"What's for dinner tonight?",
["sushi", "pasta", "tacos", "ugh I'm too hungry to think, whatever you want"],
);
```
# Proposal
When TypeScript infers type arguments to type parameters based on actual call arguments, we walk through parameters with missing corresponding arguments and infer from the type `undefined` to each parameter's type.
Some things to consider if we discuss this:
* How would this function with missing *properties*? Do we need to do something similar there?
* Is it always desirable to do this inference? Do we need a different inference priority?
* This behavior is correct for purely-optional parameters, but is it correct for parameters with default arguments? (I realized this hairball of a problem after posting this issue)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.