0

Ideally I'd want an explicit false when there's no error and exclude all Falsy values from error when there is an error:

type Result<T, E> = { result: T; error: false } | { result?: T; error: E }

type User = {
   name: string
}

export const someFunction = (): Result<User, string> => {
   if (Math.random() > 0.5)
      return {
         error: false,
         result: {
            name: 'John',
         },
      }

   return { error: '' } //  I'd want an empty string to not be allowed
}

const someOtherFunction = () => {
   const { error, result: user } = someFunction()
   if(error === false) { //  I'd want all Falsy values to be excluded from E, so I could have a clean type guard: if(error)
        return
    }
   console.log(user) //  User | undefined, given there's no error, should be User only
}

someOtherFunction()

But this doesn't work How can I exclude all Falsy values from my right-hand side error in a way that makes the destructuring assignment work cleanly?

Here's a minimal reproducible example.

André Casal
  • 1,012
  • 1
  • 11
  • 25
  • This isn't quite a [mre], since my IDE doesn't know what `register` is, and "this doesn't work" isn't an adequate description of the problem. Could you provide something that demonstrates exactly what you're talking about when I paste it into my own IDE? – jcalz Mar 20 '23 at 14:01
  • Yes I could, give me a minute. – André Casal Mar 20 '23 at 14:03
  • @jcalz I've added a (non working) example – André Casal Mar 20 '23 at 14:30
  • Why does your example have an error inside the `someFunction` function? You're not asking about that, are you? You can `as const` your `goodResult` initializer to clear that up. Ideally others can focus on the problem you have and not unrelated problems with the example – jcalz Mar 20 '23 at 14:44
  • And shouldn't your bottom `console.log(user)` be inside some kind of `else` clause? – jcalz Mar 20 '23 at 14:46
  • So, your problem is that `Truthy` is just `string`. TypeScript doesn't have *negated types* so there's no way to write `string & not ""`. Conditional types can only filter unions, and `string` is not a union. Either you need to only choose `E` in a way that it is a union of some always-truthy and some always-falsy types, or you need to refactor to something like [this](https://tsplay.dev/w25X9W) using a custom type guard. Does that fully address your question? If so, I could write up an answer explaining; if not, what am I missing? – jcalz Mar 20 '23 at 14:49
  • I've cleaned up the non working example. – André Casal Mar 20 '23 at 15:38
  • I'm trying to avoid a type guard, so that leaves the other option of "choose E in a way that it is a union of some always-truthy and some always-falsy types", which I'm not quite sure how that would look like. Could you please provide an example? – André Casal Mar 20 '23 at 16:06
  • Like [this](https://tsplay.dev/wOLMdN) maybe. (And again, you need that else clause at the end or it doesn't do anything useful) – jcalz Mar 20 '23 at 16:08
  • Yes, I've added a return statement to the `if` to fix it. – André Casal Mar 20 '23 at 16:09
  • `return` from what? That's not in a function. Could you please use an IDE to make sure your [mre] is actually a [mre] and tell me when you're all done? – jcalz Mar 20 '23 at 16:11
  • Apologies, I fixed my example right after my comment, but you where fast to read :) – André Casal Mar 20 '23 at 16:14
  • [That](https://tsplay.dev/wOLMdN) example locks the error string to "John", right? – André Casal Mar 20 '23 at 16:16
  • Indeed it does; you could make it `"John" | "Paul" | "Ringo" | "George" | "" | undefined` if you want, but it needs to be a union of things which are each definitely truthy or definitely falsy. Any type like `string` which contains both truthy and falsy members will not work. – jcalz Mar 20 '23 at 16:31
  • ... Actually I'll go further: it needs to be a union of literal types if you expect it to always work as the discriminant of a discriminated union; `string`, even if it were always truthy, probably wouldn't work the way you like because TS doesn't consider it a discriminant (e.g. `a` isn't a discriminant of `{a: string, b: X} | {a: number, c: Y}`). Either you need a custom type guard or you need to curate your discriminated unions. So how should we proceed here? I'd like to either write up an answer or run away screaming soon. – jcalz Mar 20 '23 at 16:32
  • But `null` | `string` [works](https://stackoverflow.com/questions/75790020/null-as-discriminator-in-discriminated-union/75790072#75790072) as a discriminator. – André Casal Mar 20 '23 at 16:34
  • Let me put it another way: taking [this](https://stackoverflow.com/questions/75790020/null-as-discriminator-in-discriminated-union/75790072#75790072) into consideration, would it be possible to exclude all Falsy values from the error type E? – André Casal Mar 20 '23 at 16:36
  • Yes, as long as a member of the union is literal you can use it to discriminate (a complexity I left out of my previous comment) but you have to check for the literal itself. `null | string` is a discriminant *if you check for `null`*. As mentioned, `string` is not always falsy so you can't check it for falsiness as a discriminant. You can check for `=== false` or `=== null` or whatever, but that's not the same as falsiness which is the point of the question. I'm about at my limit for conversation on this issue. Would you like an answer or should I disengage? – jcalz Mar 20 '23 at 16:51
  • @jcalz Please feel free to disengage as I don't think we've answered the question. Thank you for your help! – André Casal Mar 20 '23 at 17:15

0 Answers0