Skip to content

Deducing return types from boolean arguments does not work with JSDoc and default argument values #59214

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
Darker opened this issue Jul 9, 2024 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@Darker
Copy link

Darker commented Jul 9, 2024

🔎 Search Terms

🕗 Version & Regression Information

I don't know how to determine the exact version VSCode is using, but I just updated it...

⏯ Playground Link

https://www.typescriptlang.org/play/?jsx=0&filetype=js#code/C4TwDgpgBAKuEGEAWB7AlgYwgHhlCAHsBAHYAmAzlAEYooA2EAhiQDSwBiaAThcOzADKEDCnIA+KAF5Y+IqUpRg3AK7QA-Jx58oALljDR5ANwAoU6EhQAIiKYgAovPIVc7ABpziL2JJkwzUwB6ACoQ0ygQqAABYgBbMHomYigAb1oGZhIAXygANQAlCGAVbhIKADkVOOoIbgio6LAmbiY4tNsMeydvSmwMxhZ2QuLS8qqauvFc7lGyyura7igGmNmS+bS4SGR0LGwRjfHFuvYSE+52Pm40EgBzaYag0wAzFRIMYDQxKHWxgCEQLtMBAABR-eYTJbSJSqCAASjSESgKORKKCQRiwAoAFo0HcSChZmjfnMSFBzvR6GZsuYjDoKKgVPQyP8IFC6jCISRAcCsKDlGp4WZ6cAoIyUMzWRBBMpbncuWTeagQaCXkx6BQEWYMVAAIKfFQaqBIW7AfSi8VMll6zUoNkc7j6a7yqAAHwpF1MlolUttFHt7IuiqOyr2YOF5iAA

💻 Code

This problem occurs when trying to use JSDoc with TypeScript conditional types. The conditional type I created is the following:

type TypeChoice<T extends boolean, TFirst, TSecond> = T extends true ? TFirst : TSecond;

Application in plain javascript:

/**
 * @template {boolean} VReturnsNumber
 * @param {VReturnsNumber} returnsNumber 
 * @returns {TypeChoice<VReturnsNumber, number, string>}
 */
// Error in the argument: 
// Type 'boolean' is not assignable to type 'VReturnsNumber'.
//   'boolean' is assignable to the constraint of type 'VReturnsNumber',
//   but 'VReturnsNumber' could be instantiated with a different subtype of constraint 'boolean'.ts(2322)
function returnByChoice(returnsNumber = true) {
    
    // @ts-ignore
    return null;
}

// These two work fine and show the desired type
const shouldBeNumber = returnByChoice(true);
const shouldBeString = returnByChoice(false);
// Actual hint: const shouldAlsoBeNumber: string | number
// default argument is not applied
const shouldAlsoBeNumber = returnByChoice();

🙁 Actual behavior

Error on the default argument:

Error in the argument: 
Type 'boolean' is not assignable to type 'VReturnsNumber'.
  'boolean' is assignable to the constraint of type 'VReturnsNumber',
  but 'VReturnsNumber' could be instantiated with a different subtype of constraint 'boolean'.ts(2322)

Additionally, the type is unknown if no argument is provided.

🙂 Expected behavior

  • The default argument should not cause an error. At the very least, it should not cause an error when the base type for the template is a primitive type
  • The default arguments should be used to infer the template if possible

Additional information about the issue

I also tried to use this to suppress the error, but it kills the type inference in the process (the result type is always number | string):

type DecayExtends<T, X extends T> = T;
/**
 * @template {boolean} VReturnsNumber
 * @param {DecayExtends<boolean, VReturnsNumber>} returnsNumber 
 * @returns {TypeChoice<VReturnsNumber, number, string>}
 */
function returnByChoice(returnsNumber = true) {
    
    // @ts-ignore
    return null;
}
@Darker
Copy link
Author

Darker commented Jul 9, 2024

Looking more into the other issue I found, I can see that problem number 2 can be manually fixed by stating the default value. I also tried stricter template constraint, but the error still persists:

/**
 * @template {true|false} [VReturnsNumber=true]
 * @param {VReturnsNumber} returnsNumber 
 * @returns {TypeChoice<VReturnsNumber, number, string>}
 */
function returnByChoice(returnsNumber = true) {
    // @ts-ignore
    return null;
}

The error still is:

Type 'boolean' is not assignable to type 'VReturnsNumber'.
  'boolean' is assignable to the constraint of type 'VReturnsNumber', but 'VReturnsNumber' could be instantiated with a different subtype of constraint 'boolean'.ts(2322)

In this case, it is clearly wrong, as there is no way VReturnsNumber could be instantiated to anything except true or false.

@MartinJohns
Copy link
Contributor

The error is correct, because generic arguments can be provided explicitly:

returnByChoice<false>() // Why is returnsNumber true, when the type can only be false?

That is what is meant with

but 'VReturnsNumber' could be instantiated with a different subtype of constraint 'boolean'

@RyanCavanaugh
Copy link
Member

Effectively a duplicate of #58977

@MartinJohns you're my favorite "eager noobie" here btw 👍 (this is an inside joke, OP please disregard)

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jul 9, 2024
@Darker
Copy link
Author

Darker commented Jul 9, 2024

If this is a duplicate would it make sense for me to create more specific issue regarding the workaround with DecayExtends not working? It seems that workaround should not lose the value type but it does. Or is that also known issue/has a reason for not working?

@RyanCavanaugh
Copy link
Member

DecayExtends isn't going to work either because, in the presence of zero candidates, the type parameter is resolved to its constraint (it's as if you had written returnByChoice<true | false>). It's more or less the same root cause.

@typescript-bot
Copy link
Collaborator

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

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jul 13, 2024
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

4 participants