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

JavaScript Ergonomics #300

Open
krpeacock opened this issue Dec 9, 2021 · 5 comments
Open

JavaScript Ergonomics #300

krpeacock opened this issue Dec 9, 2021 · 5 comments
Assignees
Labels
enhancement New feature or request

Comments

@krpeacock
Copy link
Contributor

krpeacock commented Dec 9, 2021

Currently, there are two issues with the JavaScript / TypeScript bindings that make development inconvenient for IC developers.

  1. Options
  • Optional nested values in JavaScript are denoted idiomatically using the Optional Chaining operator (?.)
  • Our generated pattern, of a null value being represented as [] does not align with expectations of developers, as Arrays are used to represent ordered data.
  1. Variants
  • For Variants such as Result.Result, we get back a typing that is not ideal for JavaScript, and requires developers to add a significant amount of boilerplate and error handling.
  • a type of { ok : () } | { err: string } cannot be safely accessed as result.ok or result.err, but requires the user to write something like
if ('ok' in result){
 // continue
}
else if ('err' in result) {
 // handle result.err
}
* This code is unsafe, and is problematic for TypeScript in particular. Instanceof is also not reliable for determining which case is represented.

Describe the solution you'd like

  • Replace options with Optional Chaining
    • may require additional support in the JS Agent for converting null back to the candid type
  • Handle variants with a "kind" attribute that will always be present and checkable in variants
@krpeacock krpeacock added the enhancement New feature or request label Dec 9, 2021
@nomeata
Copy link
Collaborator

nomeata commented Dec 9, 2021

Optional nested values in JavaScript are denoted idiomatically using the Optional Chaining operator (?.)

I don't quite follow. The problem with nested opt is which values to use to represent them; ?. is an operator on values.

Which values would you use for null, opt null and opt 5 at type opt opt int?

@rossberg
Copy link
Contributor

I can see that the ability to use a switch over a variant would be desirable. But can you elaborate why this code is "unsafe and problematic" in TypeScript?

How would you suggest to represent argumentless variants like variant {a; b}, or more interestingly, mixed ones like variant {a; b : nat}? TypeScript would idiomatically represent the former as "a" | "b", but that representation would not allow using a single switch for the latter.

@krpeacock
Copy link
Contributor Author

Which values would you use for null, opt null and opt 5 at type opt opt int?

null would be null. opt null would be undefined | null. opt 5 would be undefined | number | bigint, and I think I would flatten nested opts to simply undefined | <T>. We can still nest them appropriately as arrays over the wire, but at the end of the day there either is a value at the end of the chain, or there isn't. Unless I am failing to imagine a case here

@krpeacock
Copy link
Contributor Author

I can see that the ability to use a switch over a variant would be desirable. But can you elaborate why this code is "unsafe and problematic" in TypeScript?

instanceof interacts poorly with JavaScript imports, and a compatible interface from a different package will not always yield consistent results. Different versions of @dfinity/agent and @dfinity/principal can end up with instanceof Principal not behaving as expected. This is why we provide methods like Principal.prototype.isPrincipal(). I wouldn't forbid someone from using it, but relying on instanceof for non-native types is a pattern that will tend to introduce bugs for devs.

How would you suggest to represent argumentless variants like variant {a; b}, or more interestingly, mixed ones like variant {a; b : nat}? TypeScript would idiomatically represent the former as "a" | "b", but that representation would not allow using a single switch for the latter.

I would represent argument-less variants as strings of the variant name. This handles mixed variants well enough, and allows for strong type inspection into example.kind

type exampleType = { kind: "a" } | { kind: {b: bigint} }

image

@krpeacock
Copy link
Contributor Author

After discussing with @chenyan-dfinity , we agreed that nested opts should be typed the same as they are now, with nested empty arrays

ninegua pushed a commit to ninegua/candid that referenced this issue Apr 22, 2022
* Be less specific about authentication methods

* Keep attestation option
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants