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

How to check if an unknown object is a Union object and narrow type? #16

Open
ghost opened this issue Feb 5, 2021 · 2 comments
Open

Comments

@ghost
Copy link

ghost commented Feb 5, 2021

  • I'm submitting a ...
    [ ] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
    [x] question about how to use this project

If I have some arbitrary union like:

const MyUnion = Union({
  Str: of<string>(),
  Num: of<number>()
})
type MyUnion = typeof MyUnion.T

And I have a function that maybe throws a MyUnion:

try {
  foobar();
}
catch (err: unknown) {
  // ?? how to tell if `err` is a `MyUnion` here?
}

How can I tell if the returned value is a MyUnion? There doesn't seem to be any function in the API like MyUnion.is(maybeAUnion) that could narrow the type.

@twop
Copy link
Owner

twop commented Mar 18, 2021

Hi @kahnc, sorry for such a long response. Needed to take a break from being active on GitHub.

That is a very good question!

In fact I was thinking about this API a lot. It boils down to two choices:

  1. Make a stable identifier that can be transferred over the wire or saved to a database or on the disc
  2. Transient and only works within an execution context, that does include inability to transfer values to a WebWorker (because it is a different execution context)

Of course, I like 1st option much better, but it is also harder to do. Here are my thoughts on this

Option 1: Safe for persistence

id is just a concat of strings

const id = ["Str", "Num"].sort().join('-'); // "Num-Str"

const unionValue = {
 k = "Str",
 a = 1,
 p0= "some value", p1=undefined, p2=undefined,
 id = "Num-Str" // <-------------
}


MyUnion.is = (val) => val.id === "Num-Str"

Which is probably good enough. E.g. Identity of a Union type is defined by the set of cases (choices) it has.

But it does seems kinda wasteful to pass the whole "Num-Str" string around for each value. I would love to use some hashing function, ideally that maps to just an int, because there is very low chance for hash collision within a single application.

const idStr = ["Str", "Num"].sort().join('-'); // "Num-Str"
const id = someIntHash(idStr); // 455138

Small int (<2**31) is stored by value in most JS engines vs floats and big integers are allocated on the heap. Thus int would be way more efficient.

The only problem that I haven't done my research how to hash a string into an int.

Option 2: valid only within an execution context

const generateId = (()=>{
 let id = 1;
 return () => id++
}) ()

const unionValue = {
 k = "Str",
 a = 1,
 p0= "some value", p1=undefined, p2=undefined,
 id =1 // <------------- assigned at Union type creation
}

This solution is super easy to make but very brittle. I use web workers a lot and inability to be able pass union values safely seems off to me.

Option 3: work around

You can unblock yourself by doing a simple workaround

const errStrId = "super unique string that identifies this error"

type WrappedError = {
  type: errStrId
  val = MyUnion 
}

try {
  foobar();
}
catch (err: unknown) {
  if ((err as WrappedError).type === errStrId) {
     // do something with err.val
 }
}

Please let me know what do you thing about Option 1 and Option 2

Thanks.

@ghost
Copy link
Author

ghost commented Jun 18, 2021

Hey @twop, thanks for the eventual reply! I eventually did just find another way to unblock myself, but I'd like an elegant solution to this. I'm tending towards Option 1, for persistence etc.

I'd want to make sure that the chance of a collision is sufficiently low that I can feel confident in a program's correctness. I haven't done any research on hashing strings either so I don't really know enough to say whether hashing would be a good strategy here.

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

1 participant