profile
viewpoint
Philipp von Weitershausen philikon The Internets http://philikon.de

mjohnston/react-native-webpack-server 946

Build React Native apps with Webpack

andreasgal/B2G 921

Boot to Gecko aims to create a complete, standalone operating system for the open web.

philikon/BarTab 64

Firefox add-on: Drink now, pay later: put your tabs on your bar tab!

philikon/MockHttpRequest 62

A mock implementation of XMLHttpRequest for unit testing

joneschrisg/mozilla-central 33

Unofficial import of Mozilla's mozilla-central hg repository using hg-git

andreasgal/wifi.js 7

B2G wifi stack

philikon/BarTabLite 6

BarTab Lite

philikon/BarTabUnflash 6

Firefox extension: Unload tabs containing Flash as soon as you leave them (using BarTab)

philikon/feynmf 4

A combined LaTeX/Metafont package for easy drawing of professional quality Feynman diagrams.

philikon/diplomarbeitsvorlage 3

My enhanced LaTeX thesis template for TU Dresden. Includes Makefile, enhanced version of feynMF and many other improvements.

issue commentstephenh/ts-proto

Decoding an encoded object may not always decode back the original object

The problem is definitely on the encoding side, where we'll not even emit the bytes for myType if its value is 0. @stephenh, what's the reason for that behavior? I definitely think we should change it for oneof=unions, but I'd argue it should probably be changed for oneof=properties as well...

davidvuong

comment created time in 17 days

issue commentstephenh/ts-proto

Decoding an encoded object may not always decode back the original object

Actually, this somewhat consistent with the default (oneof=properties) behavior.

const eg3 = Example.encode({
  name: "David",
  myType: TypeA.x,
  enabled: undefined,
}).finish();
console.log(Example.decode(eg3));

also yields

{ name: 'David' }
davidvuong

comment created time in 17 days

issue commentstephenh/ts-proto

Decoding an encoded object may not always decode back the original object

Nice catch. Yes, I think that's a bug. Will provide a fix.

davidvuong

comment created time in 17 days

issue closedstephenh/ts-proto

Immitate protobuf.js for oneof and non-scalar behavior re: field optionality

I don't find ts-proto's generated types particularly helpful because it forces me to always provide all fields (even if I set it to undefined), which is especially annoying when you have huge nested oneofs.

As an example, consider the following proto:

message Person {
  string name = 1;
  uint32 age = 2;
  oneof legal_document {
    Passport passport = 3;
    DriversLicense dl = 4;
    GovtIssuedId govt_id  = 5;
  }
}

ts-proto translates this to

export interface Person {
  name: string;
  age: number;
  passport: Passport | undefined;
  dl: DriversLicense | undefined;
  GovtIssuedId: govtId | undefined;
}

If I wanted to encode one of these messages, I'd have specify all fields:

Person.encode({
  name: "Bob",
  age: 37,
  passport: undefined,
  dl: undefined,
  govId: undefined,
})

That is tedious and you can imagine with large messages, especially nested oneofs, it becomes entirely impractical. I'm also debating whether we should need to specify age and name at this stage. proto3 says that all fields are essentially optional, though google.protobuf.*Value is the de-facto way to explicitly declare an optional value.

(Also, I personally would also consider code that explicitly sets a value to undefined to be bad form. IMHO, undefined is a fallback value that the JS/TS language provides when the thing you're trying to access cannot be found or was not defined. Code that explicitly wants to provide a null value should use null. That's my interpretation of JS's built-in nullish types.)

I think pbts actually gets this right. It would translate the above to

                interface IPerson {
                    name?: (string|null);
                    age?: (number|null);
                    passport?: (Passport|null);
                    dl?: (DriversLicense|null);
                    govtId?: (GovtIssuedId|null);
                }

which is the interface that consumed by Person.encode(). This means I can write

Person.encode({name: "Bob"})

and it's perfectly valid. We can debate whether name and age should be optional and nullable (probably not), but all the fields in the oneof definitely should be! And for clarity, I would argue that any value that's not a basic type should be optional too, to save tedious typing, having to set them to null (or, worse, undefined, which as said above, is not a value I'd ever expect application code to have to set, it's something to check for).

I'm happy to help with the implementation, and I'm also happy to hide any changes behind a compiler flag if that's preferable.

closed time in a month

philikon

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof and non-scalar behavior re: field optionality

It is, thanks a bunch!

philikon

comment created time in a month

delete branch philikon/ts-proto

delete branch : oneofUnions

delete time in a month

pull request commentstephenh/ts-proto

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

@stephenh any chance you could look at this over the next few days?

philikon

comment created time in a month

delete branch philikon/ts-proto

delete branch : broken_imports

delete time in 2 months

delete branch philikon/ts-proto

delete branch : flags

delete time in 2 months

delete branch philikon/ts-proto

delete branch : forEach

delete time in 2 months

delete branch philikon/ts-proto

delete branch : prepare

delete time in 2 months

create barnchphilikon/ts-proto

branch : oneofUnions-build

created branch time in 2 months

push eventphilikon/ts-proto

Json Choi

commit sha 8429d7552349be7f9aca868c69b94e6c44e2e788

fix typo in README (#91)

view details

philikon

commit sha 8938c511f5f0c19d17a55062a4518b92289eaa76

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties Addresses https://github.com/stephenh/ts-proto/issues/74

view details

push time in 2 months

create barnchphilikon/ts-proto

branch : prepare

created branch time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 2cf9ebe66481f88f6d094acd55a5fe376c72072d

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties Addresses https://github.com/stephenh/ts-proto/issues/74

view details

push time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 5798b3ae84034028d3cbc3495cfb250b49977da3

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties Addresses https://github.com/stephenh/ts-proto/issues/74

view details

push time in 2 months

pull request commentstephenh/ts-proto

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

I think I understand the fromPartial concern now. @cliedeman, your example illustrates it perfectly and I think your proposed solution

T extends { $case: string }
  ? { [K in keyof Omit<T, '$case'>]?: DeepPartial<T[K]> } & { $case: T['$case'] }

works great. I think it's fine to require that if choice is filled out, you have to provide $case, but you don't have to provide anything else. I'll update the PR accordingly.

I'll also remove the optionality of message payloads inside the oneof as discussed.

philikon

comment created time in 2 months

pull request commentstephenh/ts-proto

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

(Btw, in your example you can do switch (data.choice?.$case) and avoid the outermost if that way.)

philikon

comment created time in 2 months

pull request commentstephenh/ts-proto

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

the From Partial allows the user to omit the case.

Yes, I think that's the whole point of fromPartial yes? :)

I think that the generated code should actually be ` | { $case: 'aMessage'; aMessage: PleaseChoose_Submessage /* undefined removed */ }

Hmm, I need to think about this some more, but I think you might be right. Either you chose the aMessage case and then you need to provide the value, or you didn't want to provide a value at all, but then there's no point to provide a $case. Yeah, I think that makes sense, but before I go back into the code, I might want @stephenh and @timostamm to weigh in.

philikon

comment created time in 2 months

pull request commentstephenh/ts-proto

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

I can't see what the failure is, circleci wants me to log in. :(

philikon

comment created time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 154e2fb30002db5d93dd2b1f751b11de572d6f26

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties Addresses https://github.com/stephenh/ts-proto/issues/74

view details

push time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof and non-scalar behavior re: field optionality

@cliedeman yeah, sorry, too many distractions, but PR is up now.

philikon

comment created time in 2 months

PR opened stephenh/ts-proto

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

Addresses https://github.com/stephenh/ts-proto/issues/74

+1523 -32

0 comment

20 changed files

pr created time in 2 months

push eventphilikon/ts-proto

Philipp von Weitershausen

commit sha c5a62b76c867bd87a81b95e74cc6e601402c7655

small improvements to code clarity (#90) * use forEach to iterate over arrays with index, rather than keeping count manually * collect boolean parameters keepValueType, repeated into a typesOptions argument for better readability

view details

philikon

commit sha 6fcfe24034e6a8eb3d92135f268ed23b912ff780

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties Addresses https://github.com/stephenh/ts-proto/issues/74

view details

push time in 2 months

delete branch philikon/ts-proto

delete branch : clarity

delete time in 2 months

push eventphilikon/ts-proto

Philipp von Weitershausen

commit sha cff3718f18cabf7be0c22ceb8e88847b1b693d31

Compiler flag useOptionals=true turns non-scalar fields into optional properties (#88) * Don't generate default entries in the base object when the value would be undefined * Compiler flag useOptionals=true turns non-scalar fields into optional properties Addresses first part of https://github.com/stephenh/ts-proto/issues/74 * Make messageToTypeName 'options' parameter required

view details

Stephen Haberman

commit sha 630a8031eb8fb6d7ac0c28362536ec870f97a5eb

v1.25.0

view details

Stephen Haberman

commit sha d27bb88098c9772d5624175d70c682894c15ba74

Remove note about prototype-driven defaults.

view details

philikon

commit sha 0e3c5356becdb09762c801134dbcb6cb17bb7ebf

use forEach to iterate over arrays with index, rather than keeping count manually

view details

philikon

commit sha ac477dd1adac2171c618bf3fb1bda18ff4be37ce

collect boolean parameters keepValueType, repeated into a typesOptions argument for better readability

view details

philikon

commit sha 5f781fb63dde49405a09296ec8ac537b2513d96a

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

view details

push time in 2 months

PR opened stephenh/ts-proto

small improvements to code clarity
  • replace for...of loops + manual index count with forEach loops which provide us with item + index out of the box
  • replace boolean parameters with an object for better readability, as discussed in previous PR
+38 -47

0 comment

2 changed files

pr created time in 2 months

create barnchphilikon/ts-proto

branch : clarity

created branch time in 2 months

issue commentstephenh/ts-proto

Bash Scripts Don't work on Mac

All that's required is a bash version that's slightly more modern than the iPhone... The scripts work on Mac if you brew install bash. (Note how the shebang refers to /usr/bin/env bash and doesn't hardcode /bin/bash.)

cliedeman

comment created time in 2 months

delete branch philikon/ts-proto

delete branch : nonScalarOptionals

delete time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 52f2d46986eba5c1be51f93eeb31fef5b50470ae

Compiler flag oneof=unions generates type unions for oneof fields instead of individual properties

view details

push time in 2 months

pull request commentstephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar fields into optional properties

Sure, the object type sgtm.

Ok I'll send a separate PR for that some time since this PR is ready for review.

philikon

comment created time in 2 months

create barnchphilikon/ts-proto

branch : oneofUnions

created branch time in 2 months

fork philikon/protobuf

Protocol Buffers - Google's data interchange format

https://developers.google.com/protocol-buffers/

fork in 2 months

create barnchphilikon/ts-proto

branch : forEach

created branch time in 2 months

pull request commentstephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar fields into optional properties

I'm not sure I like the two optional bools at the end of the messageToTypeName parameter list:

  keepValueType: boolean = false,
  repeated: boolean = false

IMHO bool params are an anti-pattern anyway because a call like messageToTypeName(typeMap, field.typeName, options, true, false) gives you little insight into what true and false are doing.

What do you think about taking an object there instead? Something like this:

export function messageToTypeName(
  typeMap: TypeMap,
  protoType: string,
  options: Options,
  typeOptions: {keepValueType?: boolean, repeated?: boolean}
): TypeName {

This would translate the above call to messageToTypeName(typeMap, field.typeName, options, {keepValueType: true, repeated: false}) (or you could even leave out repeated).

philikon

comment created time in 2 months

pull request commentstephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar fields into optional properties

Rebased on top of master, fixed linter errors, and added another commit that makes options a required param in messageToTypeName.

philikon

comment created time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 39d0d66467b4337e75b38f8b027ac5f230b99b87

Don't generate default entries in the base object when the value would be undefined

view details

philikon

commit sha 31b1c348bd96fc56d60c8f56bca386b3f82fa368

Compiler flag useOptionals=true turns non-scalar fields into optional properties Addresses first part of https://github.com/stephenh/ts-proto/issues/74

view details

philikon

commit sha aaea37f46456153ad10da5b0140a5073bd5d772c

Make messageToTypeName 'options' parameter required

view details

push time in 2 months

push eventphilikon/ts-proto

Stephen Haberman

commit sha 20436d67b641162e6538ccb62af944462811e9c9

Send initial yarn install stderr to /dev/null to avoid false warnings. (#87)

view details

Ciaran Liedeman

commit sha c4227fca9871ace528a0a372a8c62b5cd7ac0055

Feat/remove prototype (#86) * Replaced Object.create with rest spread * fixed tests * fix review feedback

view details

Stephen Haberman

commit sha 52ee1017de4ca2ff215e57364d8d2b98548be9c1

v1.24.0

view details

Stephen Haberman

commit sha d230f32acd6defe5b40af00844b56179c0eb192d

Fix wrong versions.

view details

philikon

commit sha 519d3213d8f5efb6c8184237e225c296ef26d436

Don't generate default entries in the base object when the value would be undefined

view details

philikon

commit sha 3756453ccd920e218004289730de97c9b4bd32a7

Compiler flag useOptionals=true turns non-scalar fields into optional properties Addresses first part of https://github.com/stephenh/ts-proto/issues/74

view details

philikon

commit sha 316162548bfb4b9c2b793ae437c6e58a84b88979

Make messageToTypeName 'options' parameter required

view details

push time in 2 months

Pull request review commentstephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar fields into optional properties

 export function valueTypeName(field: FieldDescriptorProto): TypeName { export function messageToTypeName(   typeMap: TypeMap,   protoType: string,+  options: (Options | null) = null,

We can. The only callsites of messageToTypeName that don't currently have access to options are requestType and responseType. We could pass options into those, though, I think. I was just shying away from a bigger change.

philikon

comment created time in 2 months

PR opened stephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar fields into optional properties

Also includes a change not to generate default entries in base objects when the value would be undefined anyway as a separate commit. Probably best to review each commit individually.

+3147 -101

0 comment

38 changed files

pr created time in 2 months

create barnchphilikon/ts-proto

branch : nonScalarOptionals

created branch time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof and non-scalar behavior re: field optionality

Thanks everyone for your comments!

Let's split the oneofs/ADTs out from the optional option? I.e. a useOptionals that defaults to false and on true sets all non-primitive fields to firstName?: string instead of firstName: string | undefined seems good as a dedicated PR.

Agreed. I already have that pulled out and will send a separate PR.

@timostamm wrote:

The idea was that oneofCase is much more unlikely to clash with a field name.

@stephenh wrote:

Fwiw I've seen kind generally/conventionally used for these sorts of type unions.

@cliedeman wrote:

You can easily sidestep collisions by using a special character like $case or something.

All good points! 'case' might clash with a field name just as much as 'none' might. I do like the idea of $case, that's a brilliant way of using an identifier that's allowed in JS/TS but not in proto! I'd be open to $kind too but part of the reason I like the word "case" is because of its close association with the switch statement. I don't feel strongly about it, though.

Possible solution (3): case: undefined

That's basically the same as an optional field, since demo.result?.$kind would resolve to undefined if demo.result was not provided.

and it seems nice to have all N clauses of "could be kind A or kind B or kind C or kind none" all handled at the "same level"

The optional field would still allow you to do that:

interface Demo1 {
   result?: { $case: 'error', error: string }
          | { $case: 'value', value: number },
}

function process1(demo: Demo1) {
    switch (demo.result?.$case) {
        case 'error':
            break;
        case 'value':
            break;
        case undefined:
            break;
        default:
            throw new Error('Should not get here!');
            break;
    }
}

I will work on a PR formulating the above approach.

philikon

comment created time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof and non-scalar behavior re: field optionality

@timostamm your proposal was this:

interface Demo {
   result: { oneofCase: 'error', error: string }
         | { oneofCase: 'value', value: number }
         | { oneofCase: 'none' },
}

I find oneofCase a bit wordy, any reason we can't just call it case? It reads quite nicely IMHO:

interface Demo {
   result: { case: 'error', error: string }
         | { case: 'value', value: number }
         | { case: 'none' },
}

But more importantly, I'm not sure I like the magic string 'none'. It might very well be a valid field name within a oneof clause! We'd have a clash.

I see two possible solutions: (1) The oneofCase values come from an enum we define, similar to how google-protobuf does it:

interface Demo {
  enum ResultCase {
    NOT_SET = 0,
    ERROR = 1, // this would correspond to the protobuf field index,
    VALUE = 2, // so it might be anything except 0
  }
  result: { case: ResultCaseERROR, error: string }
        | { case: ResultCase.VALUE, value: number },
        | { case: ResultCase.NOT_SET },
}

(2) Instead of another type union, the result property is made optional:

interface Demo {
   result?: { case: 'error', error: string }
          | { case: 'value', value: number },
}

I personally much prefer the latter due because of its brevity. Yes, consumers will have to check that demo.result isn't null-ish before switch (demo.result.case), but that seems acceptable to me. Your thoughts?

philikon

comment created time in 2 months

Pull request review commentstephenh/ts-proto

feat: Simple Objects

 function visitServices(   } } +function generateObjectInitStatement(func: FunctionSpec, fullName: string, nullPrototypes: boolean) {+  if (nullPrototypes) {+    return func.addStatement('const message = {...base%L} as %L', fullName, fullName);+  } else {+    return func.addStatement('const message = Object.create(base%L) as %L', fullName, fullName);

nit: else after return is unnecessary noise

cliedeman

comment created time in 2 months

delete branch philikon/ts-proto

delete branch : useOptionals

delete time in 2 months

PR closed stephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar + oneof fields into optional properties

Addresses https://github.com/stephenh/ts-proto/issues/74

+3180 -19

1 comment

19 changed files

philikon

pr closed time in 2 months

pull request commentstephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar + oneof fields into optional properties

Closing this in favor of a future PR that will have different flags for non-scalar optionals and better oneof typing.

philikon

comment created time in 2 months

delete branch philikon/ts-proto

delete branch : array

delete time in 2 months

issue commentstephenh/ts-proto

repeated google.protobuf.FoobarValue fields should translate to Foobar[], not Array<Foobar | undefined>

Woo thanks! I'll continue to work on #74. Let me know if you have any thoughts there, otherwise you may get a PR in the next few days or so...

philikon

comment created time in 2 months

PR opened stephenh/ts-proto

repeated google.protobuf.FoobarValue fields should translate to Foobar[], not Array<Foobar | undefined>

addresses https://github.com/stephenh/ts-proto/issues/80

+34 -23

0 comment

6 changed files

pr created time in 2 months

create barnchphilikon/ts-proto

branch : array

created branch time in 2 months

create barnchphilikon/ts-proto

branch : flags

created branch time in 2 months

issue openedstephenh/ts-proto

repeated non-scalar fields should translate to Field[], not Array<Field | undefined>

As discussed in https://github.com/stephenh/ts-proto/issues/74, repeated fields can never have nullish values. This includes non-scalar fields, which are currently translated to Array<Field | undefined>, but should be Field[].

created time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof and non-scalar behavior re: field optionality

I like the way you're going with that, @timostamm. I was going to open another ticket + PR for adding a member to decoded objects that would tell you which oneof case is populated. I had originally thought of something like this:

interface Demo {
  result: 'error' | 'value' | null,
  error?: string,
  value?: number,
}

but your proposed solution is superior because it links which result is populated to its value type.

I propose two separate compiler flags then:

  • useOptionals turns all non-scalar fields into optional properties rather than typing them as xyz | undefined (doesn't touch oneof fields)
  • oneofUnions emits oneofs as you proposed

Members of repeated fields and maps cannot be null, regardless whether the element type is a message or scalar.

Yeah, which I think means types like https://github.com/stephenh/ts-proto/blob/master/integration/simple/simple.ts#L70-L71 are not correct and should probably be fixed. Should we file a separate ticket for that?

philikon

comment created time in 2 months

PR closed stephenh/ts-proto

Compiler flag nullableOptionals=true turns message + oneof fields into optional and nullable properties

Addresses https://github.com/stephenh/ts-proto/issues/74

+3198 -32

1 comment

29 changed files

philikon

pr closed time in 2 months

pull request commentstephenh/ts-proto

Compiler flag nullableOptionals=true turns message + oneof fields into optional and nullable properties

Closing in favor of https://github.com/stephenh/ts-proto/pull/76

philikon

comment created time in 2 months

PR opened stephenh/ts-proto

Compiler flag useOptionals=true turns non-scalar + oneof fields into optinal properties

Addresses https://github.com/stephenh/ts-proto/issues/74

+3180 -19

0 comment

19 changed files

pr created time in 2 months

create barnchphilikon/ts-proto

branch : useOptionals

created branch time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof behavior, field optionality, and null values

I also care about type correctness, including strictNullChecks. I'm less concerned about the practical implications regarding checking null and undefined but I hear your point. I'd be happy with your proposal. I'll send a PR.

You mention giving non-scalars (and I'm assuming also fields within oneof clauses, which is one my motivators) this treatment. Does that mean you'd also advocate that it should apply to repeated fields? I've been going back and forth on that personally, but left them to be always non-optional and not-nullable for now (as I mentioned above, ts-proto in default mode actually has a bit of an inconsistency regarding the array value type IMHO.)

philikon

comment created time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof behavior, field optionality, and null values

Thanks @timostamm. Yes, if all fields were optional, you'd have to do a lot of x.name ?? 'fallback type checks and I agree that would get very verbose.

As mentioned above, I think my initial post wasn't as clear as it could have been. I propose the nullableOptionals compiler flag (as implemented in the PR, please have a look) makes optional and nullable

  • message fields
  • fields within oneof

With that, a variation of my original example

message Person {
  string name = 1;
  google.protobuf.Uint32Value age = 2;
  oneof legal_document {
    Passport passport = 3;
    DriversLicense dl = 4;
    GovtIssuedId govt_id  = 5;
  }
}

would translate to

export interface Person {
  name: string;
  age?: number | null;
  passport?: Passport | null;
  dl?: DriversLicense | null;
  GovtIssuedId?: govtId | null;
}

So I can do Person.encode({name: 'Bob', passport: {...}}) but I can't do Person.encode({passport: {...}}) because name is a required field. Hope that makes sense.

philikon

comment created time in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof behavior, field optionality, and null values

Hmm, should

  coins: number[];
  snacks: string[];

really be

  coins: Array<number | null>;
  snacks: Array<string | null>;

perhaps? Not that it's actually possible to encode a "hole" in a repeated field currently. If I do this using ts-proto's default options:

    const s = SimpleWithWrappers.encode({
      name: undefined,
      age: undefined,
      enabled: undefined,
      coins: [undefined, 3],
      snacks: [],
    }).finish()
    const d = SimpleWithWrappers.decode(s)

I get d = { coins: [ 0, 3 ], snacks: [] } as a result, so Array<number | undefined> may as well have been Array<number>?

philikon

comment created time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 7f462519bb030e876c9e72f52882426cf700ce4b

Compiler flag nullableOptionals=true turns message + oneof fields into optional and nullable properties

view details

push time in 2 months

PR opened stephenh/ts-proto

Compiler flag useOptionals=true makes all message and oneof fields optional and nullable properties

Addresses https://github.com/stephenh/ts-proto/issues/74

+3198 -32

0 comment

29 changed files

pr created time in 2 months

push eventphilikon/ts-proto

philikon

commit sha 7ad60eb073f753558aaad1635e3516fa0a3340ad

Compiler flag useOptionals=true makes all message and oneof fields optional and nullable properties

view details

push time in 2 months

create barnchphilikon/ts-proto

branch : nullableOptionals

created branch time in 2 months

fork philikon/ts-proto

An idiomatic protobuf generator for TypeScript

fork in 2 months

issue commentstephenh/ts-proto

Immitate protobuf.js for oneof behavior, field optionality, and null values

The drawback to making all fields optional in typescript is that it breaks strictNullChecks.

I don't quite follow. Can you elaborate with an example?

I think you guys have clarified an important distinction though. fromPartial would allow me to do the equivalent of Person.encode({name: 'Bob'}), as @timostamm explains. It's a pity that fromPartial is only generated when JSON methods are generated -- that's part of the reason why I missed that potential solution. Perhaps that's something we could change with a compiler flag? I might want fromPartial but none of the toJSON and fromJSON stuff.

But fromPartial is still different from what I was actually proposing but did a poor job defining well: making optional and nullable (a) message fields, (b) fields within aoneofclause, if thenullableOptionals=true` compiler flag were set. I have this working locally and this proto message

message SimpleWithWrappers {
  google.protobuf.StringValue name = 1;
  google.protobuf.Int32Value age = 2;
  google.protobuf.BoolValue enabled = 3;
  repeated google.protobuf.Int32Value coins = 6;
  repeated google.protobuf.StringValue snacks = 7;
}

translates to this

export interface SimpleWithWrappers {
  name?: string | null;
  age?: number | null;
  enabled?: boolean | null;
  coins: number[];
  snacks: string[];
}

const baseSimpleWithWrappers: object = {
};

// and the usual encode/decode functions

I'd be very happy with that as I'd still get type checking on all the properties that are supposed to be there, but I can either not specify at all or pass null for those that are clearly optional.

philikon

comment created time in 2 months

issue openedstephenh/ts-proto

Immitate protobuf.js for oneof behavior, field optionality, and null values

I don't find ts-proto's generated types particularly helpful because it forces me to always provide all fields (even if I set it to undefined), which is especially annoying when you have huge nested oneofs.

As an example, consider the following proto:

message Person {
  string name = 1;
  uint32 age = 2;
  oneof legal_document {
    Passport passport = 3;
    DriversLicense dl = 4;
    GovtIssuedId govt_id  = 5;
  }
}

ts-proto translates this to

export interface Person {
  name: string;
  age: number;
  passport: Passport | undefined;
  dl: DriversLicense | undefined;
  GovtIssuedId: govtId | undefined;
}

If I wanted to encode one of these messages, I'd have specify all fields:

Person.encode({
  name: "Bob",
  age: 37,
  passport: undefined,
  dl: undefined,
  govId: undefined,
})

That is tedious and you can imagine with large messages, especially nested oneofs, it becomes entirely impractical. I'm also debating whether we should need to specify age and name at this stage. proto3 says that all fields are essentially optional, though google.protobuf.*Value is the de-facto way to explicitly declare an optional value.

(Also, I personally would also consider code that explicitly sets a value to undefined to be bad form. IMHO, undefined is a fallback value that the JS/TS language provides when the thing you're trying to access cannot be found or was not defined. Code that explicitly wants to provide a null value should use null. That's my interpretation of JS's built-in nullish types.)

I think pbts actually gets this right. It would translate the above to

                interface IPerson {
                    name?: (string|null);
                    age?: (number|null);
                    passport?: (Passport|null);
                    dl?: (DriversLicense|null);
                    govtId?: (GovtIssuedId|null);
                }

which is the interface that consumed by Person.encode(). This means I can write

Person.encode({name: "Bob"})

and it's perfectly valid. We can debate whether name and age should be optional and nullable (probably not), but all the fields in the oneof definitely should be! And for clarify, I would argue that any value that's not a basic type should be optional too, to save tedious typing, having to set them to null (or, worse, undefined, which as said above, is not a value I'd ever expect application code to have to set, it's something to check for).

I'm happy to help with the implementation, and I'm also happy to hide any changes behind a compiler flag if that's preferable.

created time in 2 months

more