简体   繁体   中英

Why does TypeScript require optional parameters after required parameters?

This works:

public console(message: string | undefined, suppressLevel: boolean) {}

But this doesn't:

public console(message?: string, suppressLevel: boolean) {}

Considering the ? seems to basically be a shorthand for | undefined| undefined (and when you mouseover in VS Code it says just that), why does TypeScript make a distinction? You could explicitly pass in undefined for an optional parameter and it would be the same as not specifying it at the end.

Most importantly, this is a javascript fact that is just made more explicit by strongly typing a function signature.

Function parameters are bound positionally in the arguments list. The first passed argument is bound to the first parameter. The second passed argument is bound to the second parameter. And so on. With the optional parameters at the end , a javascript engine knows to bind the end parameters to undefined when the arguments list is too short. However many short the invocation is, that's how many of the end parameters get to be undefined .

If the optional parameters could be in the middle or at the beginning, there wouldn't be enough information at the call site to know which parameters should be bound to which arguments.

function fn(a?: number, b: number) { /* ... */ }

fn(42);

If fn 's signature were valid, fn(42) would be ambiguous. Should b be bound to 42 leaving a undefined ? Or should a be bound to 42 with b left undefined (and therefore a type error?) In this particular situation, you might want the typescript type checker to ASSUME that there is no error and instead try its hardest to find a valid interpretation of the invocation (the one in which b gets 42 ) but that would be a more costly overload resolution algorithm and there would still be cases that are ambiguous and the rules would be too complicated to be useful (ie fn2(a?: number, b: number, c?: number) { /* ... */ } with fn(42, 3.1415) )

Javascript, by having positional arguments with the trailing arguments optional, and with Typescript allowing us to explicitly annotate the signature overloads which are allowed, strike a great balance of expressiveness, reasonability (performance of the type-checker) and bug avoidance.

If overload resolution were more flexible in that way, then it necessarily have to consider more invocations to be valid which might actually be errors. Or it would put the burden on the function body to deal with all the variations completely, such as making all the parameter combinations being union types with eachother and then having to tease those apart in the code, making it harder to read.

? isn't shorthand for string | undefined string | undefined . It's an optional parameter. Meaning - you can either pass the expected value type, or not.

string | undefined string | undefined , by contrast, means you must explicitly pass a value that is either a string or undefined. The value may be undefined, but the parameter still exists.

It would be paradoxical to pass a property after an optional property - you wouldn't be able to set it, since setting it would mean setting one before it, which means its predecessor is then not optional!

public console(message: string | undefined, suppressLevel: boolean) {}

Uses the pipe ( | ) operator to declare a parameter that is either of type string or undefined . See union types for more detail.

public console(suppressLevel: boolean, message?: string) {}

Declares an optional parameter which as per docs need to follow required parameters .

Any optional parameters must follow required parameters.

So, while in practice, an optional string parameter IS indeed a parameter that is of type string or undefined (or null depending on your settings), those two are different syntax that follow separate rules. Forcing optional parameters to come after required parameters increase readability and functionality, and typescript can not just treat union types as optional since they are two distinctly different things. Hence the difference.

In TypeScript, every parameter is assumed to be required by the function. This doesn't mean that it can't be given null or undefined, but rather, when the function is called the compiler will check that the user has provided a value for each parameter. - Optional and default parameters

Optional parameters mean that you do not have to pass in anything. Your union example says it has to be a string or an undefined primitive .

class Lorem{
  public unionType(required:boolean,message: string | undefined) { }
  public optional(required:boolean, message?: string) {}
}

let lorem = new Lorem();
lorem.unionType(true);//Error missing second parameter message
lorem.unionType(true,undefined);//have to explicitly pass in undefined
lorem.optional(false);//A-OK

Since you can pass nothing in for optional arguments it makes sense that they have to be the last in the argument list.

You could explicitly pass in undefined for an optional parameter and it would be the same as not specifying it at the end.

But you cannot omit passing in undefined for a union parameter so they are not the exact same thing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM