Skip to content

required function parameters with default values #47202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
5 tasks done
VanCoding opened this issue Dec 20, 2021 · 7 comments
Closed
5 tasks done

required function parameters with default values #47202

VanCoding opened this issue Dec 20, 2021 · 7 comments

Comments

@VanCoding
Copy link

VanCoding commented Dec 20, 2021

Suggestion

🔍 Search Terms

required parameter default value non-optional function

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

It should be possible to mark a function parameter with a default value as required, so that it can't be undefined.

📃 Motivating Example

Consider the following function:

function printHelloWorld(printer = defaultPrinter){
    printer.print("hello world!")
}

It's clear that it needs a printer to work. But in this form, TypeScript would allow the following call to the function:

printHelloWorld(undefined)

Which is also why it would complain that printer is possibly undefined in strict mode. Even when the type of the parameter is explicitly defined, it can still be undefined when it has a default value. This is because an argument being optional is tightly coupled with it having a default value.

But just because an argument has a default value, it shouldn't mean that it can be undefined. It should just mean, that it's automatically assigned, if it's not provided in the function call.

In a perfect world, I'd propose to make this the default behavior, and add a feature to mark arguments that are truly optional with a "?" after the name. But since that would break existing code, I propose the opposite: Marking required properties with a "!".

Example:

function printHelloWorld(printer! = defaultPrinter){
    printer.print("hello world!")
}
printHelloWorld(undefined) // this should not compile

💻 Use Cases

I like to make function dependencies overridable by optional parameters that default to the main implementation, like in the example above, or grouped to an object like this:

function someComplexFunction(arg1: number, arg2: string, dependencies! = {dependency1,dependency2}){
    dependency1.foo(arg1);
    dependency2.bar(arg2);
}

This makes the function unit testable in a very easy way, and doesn't need a dependency injection framework for the real application.
But this currently only works with strictNullChecks turned off, because otherwise typescript would complain that the dependencies could be undefined.

@MartinJohns
Copy link
Contributor

This sounds like a duplicate of #44548. At least your issue would be solved by #44548.

@fatcerberus
Copy link

For the record, a parameter with a default value can't be undefined inside the function:

C:\Users\fatce>node
Welcome to Node.js v17.3.0.
Type ".help" for more information.
> function foo(x = 812) { console.log(x); }
undefined
> foo()
812
undefined
> foo(undefined)
812
undefined
>

@VanCoding
Copy link
Author

@fatcerberus wow, I didn't realize that. That makes it even more obvious that we need to change something! Probably even without new syntax.

@fatcerberus
Copy link

@VanCoding It’s actually useful because it lets you omit arguments in the middle of a call if they have default values while still providing explicit values for later ones. If it were an error to pass undefined, that wouldn’t be possible.

@VanCoding
Copy link
Author

VanCoding commented Dec 20, 2021

oooohhh! Now I understand, the type is different inside the function from outside the function. Yes, this makes sense!

I only now realise that my initial example would compile, because inside the function the printer cannot be undefined.
So my use cases are already supported o.o

I think what initially led me to believe that there's a problem is that Parameters<typeof myfunction> always gave me a union with undefined for those arguments. But that's of course, because it's looked at from outside the function and can easily be worked around using Exclude<Parameters<typeof myfunction>[0],undefined>

We can close this now. Thanks guys!

@Maverick283
Copy link

For the record, a parameter with a default value can't be undefined inside the function

Well strictly speaking it can be undefined, right?

> function foo(x = undefined) {console.log(x)}
undefined
> foo(5)
5
undefined
> foo()
undefined
undefined
> 

Nevertheless, going back to the original point that @VanCoding made, what can one do in strict mode for default values of parameters?

Say I have the following code

interface Foo {
  name: string
  y?: number
}

function bar({ name, y = 0 }: Foo): string {
    return name.substring(y)
}

then this would result in a compiler error like

Argument type number | undefined is not assignable to parameter type number

And it was made clear that y is always going to be a number because of either the type of Foo['y'] or the type of the default value as fallback.

So why is this an error?

PS: Sorry if I should have opened a new issue or put this anywhere else, I am new here!

@MartinJohns
Copy link
Contributor

then this would result in a compiler error like

It would not. It works just fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants