Skip to content

Optional types nullability - not working correctly for TS #1779

Closed
@martin-traverse

Description

@martin-traverse

protobuf.js version: 7.0.0

I don't think optional types are working correctly. My understanding is that optional types (including proto3 optionals) should have a type signature of "|null|undefined", while non-optional types should just have the base type signature. Currently, this is being applied to member definitions in JS, but not to the property type hints set at the class level. This cascades through in the TS, where optional types are respected in the concrete type definitions, but not the interface definitions where everything is nullable.

Is this diference in the type definitions intentional, or not? Even though concrete classes are returned from API calls, the nested types they are built from use interfaces. So, the net result is that our web developers still have to treat everything as nullable, regardless of what is defined as optional in the original proto files. The behviour we would like is that optional fields show as nullable, while non-optional fields do not and are supplied a default value if they are missing. I have include some example outputs below.

The root cause seems to be in static.js in the CLI, where nullability for class property comments is defined differently than for members. I will link a PR to change this, which is fairly simple. However, I realise this is a change to API and may not be desirable for everyone! So my questions are:

  1. Does this all make sense and some sort of patch is needed, or have I missed something obvious?!

and

  1. Assuming a fix is helpful in some cases, should it be guarded behind a CLI option? If so what that be?

Sample proto file

syntax = "proto3";
package tracdap.api;
...

message FileWriteRequest {

    /// Tenant code for the requested operation, always required
    string tenant = 1;

    /// Prior object/tag version to use for update operations
    optional metadata.TagSelector priorVersion = 2;
    ...

Sample output which shows the problem

JS file has "tenant" as nullable in the object properties, but not the member definition

        api.FileWriteRequest = (function() {

            /**
             * Properties of a FileWriteRequest.
             * @memberof tracdap.api
             * @interface IFileWriteRequest
             * @property {string|null} [tenant] Tenant code for the requested operation, always required
             * @property {tracdap.metadata.ITagSelector|null} [priorVersion] Prior object/tag version to use for update operations
            
            ...
            
            /**
             * Tenant code for the requested operation, always required
             * @member {string} tenant
             * @memberof tracdap.api.FileWriteRequest
             * @instance
             */
            FileWriteRequest.prototype.tenant = "";

            /**
             * Prior object/tag version to use for update operations
             * @member {tracdap.metadata.ITagSelector|null|undefined} priorVersion
             * @memberof tracdap.api.FileWriteRequest
             * @instance
             */
            FileWriteRequest.prototype.priorVersion = null;

TS file has "tenant" nullable in the interface, but not the concrete class

    interface IFileWriteRequest {

        /** Tenant code for the requested operation, always required */
        tenant?: (string|null);

        /** Prior object/tag version to use for update operations */
        priorVersion?: (tracdap.metadata.ITagSelector|null);
   
   ...
   
    class FileWriteRequest implements IFileWriteRequest {

        /** Tenant code for the requested operation, always required */
        public tenant: string;

        /** Prior object/tag version to use for update operations */
        public priorVersion?: (tracdap.metadata.ITagSelector|null);

After the change, the JS properties line up with the member definition and the TS interface lines up with the concrete class.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions