Skip to content

type alternatives in arguments become function alternatives and are then inferred to a never argument. #61186

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
sparecycles opened this issue Feb 15, 2025 Β· 5 comments

Comments

@sparecycles
Copy link
Contributor

sparecycles commented Feb 15, 2025 β€’

πŸ”Ž Search Terms

never alternative types ts2345

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.3#code/GYVwdgxgLglg9mABANzjAJgeQE4Dk5QA8AKogLwproB8AFAJQBciA3osM6QKYAeUXYdAGdKGRAH5EDctVHpEzWgENsAc070ZcxAF9WAKESJsXKCGxIwIADbWAhAG59O-fqgBPAA5dEAQQBC5IgARL7BiAA+If7BTvoQCEJQiADuMFAAFgDKMAC2ntZcxF4+FKgYOPhEocF09HFpmTn5hcXeAHTAtDX1rglgSanpGQBqVG2lcpUEhOU0DE6No+MlnQt9iclLvtb8FkqwyEUlQXPTRAF1DcM7e2AHMEcTa73bu1z7h8cdXT1xAPT-RAebxwYBDTK3D73L4TZhsDhSZRqZg9LRzTRRWjI9TRYKaMiyDG6fSA4ElMEQjJQz6Pb5cTqKFS4sBcI7YAlEqhOIA

πŸ’» Code

function voidOrNot<T = void>(): { f: T extends void ? () => void : (arg: T) => void } {
  return null!;
}

const withSimpleType = voidOrNot<"A">();
withSimpleType.f("A"); // OK

const withVoidType = voidOrNot<void>();
withVoidType.f(); // OK

type AB = "A" | "B";

const withAlternativeType = voidOrNot<AB>();

withAlternativeType.f(); // error
withAlternativeType.f("A"); // error

πŸ™ Actual behavior

Image
Image
Image

πŸ™‚ Expected behavior

f(arg: AB) => void is not expanded to (f(arg: A) => void) | (f(arg: B) => void)... or that it's still callable if it is.

Additional information about the issue

please feel free to suggest a better issue title, I'm having trouble picking a good name today!

@Andarist
Copy link
Contributor

You might have wanted to use a non-distributive conditional type there:

function voidOrNot<T = void>(): { f: [T] extends [void] ? () => void : (arg: T) => void } {
  return null!;
}

@sparecycles
Copy link
Contributor Author

sparecycles commented Feb 15, 2025 β€’

@Andarist that solved my issue, thanks!


Thinking about this.... I left out that the oldest version of typescript (3.3.3) in the Playground changes "A"|"B" to "A"&"B"

Image

(which was also wrong).


If conditional types are meant to distribute, wouldn't transforming (arg: "A"|"B") => void to ((arg: "A") => void) & ((arg: "B") => void) be the correct way to do it (with & instead of |)?

Image

@sparecycles
Copy link
Contributor Author

Searched more, looks like this is a duplicate of, or related to

@Andarist
Copy link
Contributor

If conditional types are meant to distribute, wouldn't transforming (arg: "A"|"B") => void to ((arg: "A") => void) & ((arg: "B") => void) be the correct way to do it (with & instead of |)?

That might be what you want to get in certain situations but the compiler can't predict your intention. A union distributes into another union

@sparecycles
Copy link
Contributor Author

I'm not convinced this is the best language decision, but thanks again for the tight workaround!

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

2 participants