Skip to content

Generated types allow assigning wrong message type if it is a superset of the target type  #551

Closed
@LinearSpoon

Description

There is no compile error in const a: ProtoA = new ProtoB() if the fields in ProtoA are a subset of the fields in ProtoB.

example.proto

syntax = "proto3";

message Foo {
    int32 foo = 1;
}

message NotFoo {
    int32 foo = 2;  // Different tag number
}

// Foo with an extra field
message FooAndBar {
    int32 foo = 1;
    int32 bar = 2;  // Extra field
}

example.ts

import { Foo, FooAndBar, NotFoo } from "./example_pb.js";

// Unsafe - No compile time error
const a0: Foo = new FooAndBar();
const a1: Foo = new NotFoo();
const a2: NotFoo = new Foo();
const a3: NotFoo = new FooAndBar();

// Good - compile time error
const a4: FooAndBar = new Foo();  // Property 'bar' is missing in type 'Foo' but required in type 'FooAndBar'.
const a5: FooAndBar = new NotFoo();  // Property 'bar' is missing in type 'NotFoo' but required in type 'FooAndBar'.

// This also affects function parameters
function example(foo: Foo) { }
example(new NotFoo());  // Unsafe - No error

An example fix would be adding a dummy private variable to to every generated proto. TypeScript doesn't output any code when compiling this, so there should be no runtime implications (Playground Link).

Relevant TypeScript docs:

When an instance of a class is checked for compatibility, if the target type contains a private member, then the source type must also contain a private member that originated from the same class.

export class Foo extends Message<Foo> {
  private unused: any;

  // Existing generated code...
}

// Good - Compile time error
// Type 'FooAndBar' is not assignable to type 'Foo'.
//   Types have separate declarations of a private property 'unused'.
const a0: Foo = new FooAndBar();

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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