Skip to content
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

TS (2322) Function signature overloading not being correctly determined #35338

Closed
cjativa opened this issue Nov 25, 2019 · 6 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@cjativa
Copy link

cjativa commented Nov 25, 2019

I have provided an interface containing two function signatures for my arrow function. The function returns different value types depending on whether or not the optional parameter had been provided.

If just the first parameter is provided (a string), then the return type of function will be just a string.
If the first parameter and the optional second parameter (a boolean being true) is also provided, then the return type of the function should be a random number.

I'd like to understand how I can inform the TypeScript compiler (or supress the warning) that the function will return either a string or number and allow it to correctly determine this at run time, thus allowing for the proper return type when the code is used.

Or perhaps my interface that is providing the function signature for my function is incorrect and I'd like to understand why it may be incorrect -- but because the function can be executed and return the proper value -- as seen in the playground link below -- I believe it is a bug.

TypeScript Version: 3.7.2

Search Terms:
TypeScript function overloading optional parameters
TypeScript function overloading arrow functions

Code

interface TicketOrderPayload {
	(ticketUUID: string): string;
	(ticketUUID: string, getOrderInformation: true): number
}

const retrieveAssociatedOrderForTicket: TicketOrderPayload = (ticketUUID: string, getOrderInformation?: boolean) => {

    // If the getOrderInformation parameter was provided and is true, return a number
	if (getOrderInformation) {
		return Math.random();
	}

    // Otherwise, return the ticketUUID which is just a string
	return ticketUUID;
}

const thisWillBeAString: string = retrieveAssociatedOrderForTicket('my-random-uuid');
const thisWillBeANumber: number = retrieveAssociatedOrderForTicket('my-random-uuid', true);

console.log((typeof thisWillBeAString === 'string') ? true : false);
console.log(typeof thisWillBeANumber === 'number' ? true: false);

Expected behavior:
This should not result in the function not being assignable to the interface being used. No error should be raised here since I've defined in the function signature that either of these two return types are possible.

The return types are also being correctly interpreted identified when I call the function as well -- so things should be functioning as I expect. Otherwise, I wouldn't be able to provide the expected types to the assigned variables in

const thisWillBeAString: string = retrieveAssociatedOrderForTicket('my-random-uuid');
const thisWillBeANumber: number = retrieveAssociatedOrderForTicket('my-random-uuid', true);

Actual behavior:
I receive an error

Type '(ticketUUID: string, getOrderInformation?: boolean | undefined) => string | number' is not assignable to type 'TicketOrderPayload'.
  Type 'string | number' is not assignable to type 'string'.
    Type 'number' is not assignable to type 'string'.(2322)

Playground Link:
Playground Link

@jcalz
Copy link
Contributor

jcalz commented Nov 26, 2019

Duplicate of #33482

@AlansCodeLog
Copy link

AlansCodeLog commented Nov 26, 2019

I just ran into this as well. Very similar situation. I wanted to specify that if a callback was provided as a second param, that the function could return void (because it would never throw). I do not really understand the linked issue that well. All I can say is I too expected this to work like a regular function overload and the error was very weird in my case (since SomeType was really several different types it was hard to debug what was going on).

I got around it by writing the overloads like this, or is there some more idiomatic way? :
type MyType = {
	foo:
		((str: string) => SomeType)
		| ((str: string, cb?: SomeCallbackType) => SomeType | void)
}

Nevermind that doesn't work either, when it's assigned the type is not narrowed to just SomeType when you'd expect

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Nov 26, 2019
@RyanCavanaugh
Copy link
Member

TypeScript can't analyze the function body to determine which codepaths get taken during certain overload cases, so it can only look at the overall inferred return type, which is indeed wider than the specified return type. It isn't possible to write this assignment pattern without a type assertion in at least one location.

@AlansCodeLog
Copy link

Nevermind, ignore me, I figured out the problem. It was with the try / catch block. I was returning inside try, so typescript thought the function could only ever return void.

PS: I think I have understood the problem with these types of overloads after reading #33482 a couple of times, but still think it's a bit weird.

@cjativa
Copy link
Author

cjativa commented Nov 27, 2019

@RyanCavanaugh Thank you for the explanation! That makes total sense that TypeScript wouldn't be able to determine which codepaths get taken during specific overload cases and so can only look at the overall inferred type.

Would you be able to give me an example of how a type assertion would help me achieve the pattern I'm looking for? Are you saying I'd have to do type assertion on the returned value myself?

It's the function return type declaration itself that is giving the error so I'm not sure where the type assertion would come into play.

@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

5 participants