question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Update `oneof` representation

See original GitHub issue
import { getOneofValue } from '@protobuf-ts/runtime';

interface ISomeMessage {
    foo: string;
}

type TSomeMessage = {
    foo: string;
}

declare var oneofWithInterface: {
    oneofKind: "a";
    a: ISomeMessage;
} | {
    oneofKind: undefined;
};

declare var oneofWithType: {
    oneofKind: "a";
    a: TSomeMessage;
} | {
    oneofKind: undefined;
};

const t = getOneofValue(oneofWithType, 'a'); // TSomeMessage | undefined
const i = getOneofValue(oneofWithInterface, 'a'); // TS error
// Argument of type '{ oneofKind: "a"; a: ISomeMessage; } | { oneofKind: undefined; }' is not assignable to parameter of type 'UnknownOneofGroup'.
//   Type '{ oneofKind: "a"; a: ISomeMessage; }' is not assignable to type 'UnknownOneofGroup'.
//     Property 'a' is incompatible with index signature.
//       Type 'ISomeMessage' is not assignable to type 'string | number | bigint | boolean | Uint8Array | UnknownMessage | undefined'.
//         Type 'ISomeMessage' is not assignable to type 'UnknownMessage'.
//           Index signature is missing in type 'ISomeMessage'.(2345)

The issue seems like some discrepancy between a type and interface and the use of index signatures. As shown above the TSomeMessage type works just fine, but the identical interface causes issues. The issue can be solved in one of two ways:

  1. Change the code generation to output types for Messages. (this seems bad)
  2. Change UnknownMessage to be an interface. (this seems much better)

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:1
  • Comments:19 (17 by maintainers)

github_iconTop GitHub Comments

2reactions
jcreadycommented, Apr 29, 2022

This is kind of random, but I found a way to generically convert between the current oneof shape and the new one (see it in action: Playground):

type OldExampleOneof =
  | { oneofKind: 'a', a: string }
  | { oneofKind: 'b', b: number }
  | { oneofKind: 'c', c: boolean }
  | { oneofKind: undefined };

type NewExampleOneof =
  | { kind: 'a', value: string }
  | { kind: 'b', value: number }
  | { kind: 'c', value: boolean }
  | { kind: undefined };

type OneofHelper<
    T,
    K extends T extends { oneofKind: keyof T } ? T['oneofKind'] : never
> = T extends { oneofKind: K } ? T[K] : never;
export function helpfulOneof<
    T extends { oneofKind: string | undefined; [k: string]: unknown },
    K extends T extends { oneofKind: keyof T } ? T['oneofKind'] : never,
    M extends { [k in K]: { kind: k; value: OneofHelper<T, k> } }
>(oneof: T): M[keyof M] | { kind: undefined } {
    const kind = oneof.oneofKind;
    if (!kind) {
        return { kind: undefined };
    }
    const value = oneof[kind];
    // @ts-ignore
    return { kind, value };
}

type FromOneofHelper<
    T,
    K extends T extends { kind: string } ? T['kind'] : never
> = T extends { kind: K; value: unknown } ? T['value'] : never;
export function fromHelpfulOneof<
    T extends { kind: string | undefined; value?: unknown },
    K extends T extends { kind: string } ? T['kind'] : never,
    M extends { [k in K]: { oneofKind: k } & { [p in k]: FromOneofHelper<T, k> } }
>(oneof: T): M[keyof M] | { oneofKind: undefined } {
    const oneofKind = oneof.kind;
    if (!oneofKind) {
        return { oneofKind: undefined };
    }
    // @ts-ignore
    return { oneofKind, [oneofKind]: oneof.value };
}

function onlyOldOneof(example: OldExampleOneof) {}
function onlyNewOneof(example: NewExampleOneof) {}

declare const oldExample: OldExampleOneof;
declare const newExample: NewExampleOneof;

// old to new
const oldToNew = helpfulOneof(oldExample);
onlyNewOneof(oldToNew);

// new to old
const newToOld = fromHelpfulOneof(newExample);
onlyOldOneof(newToOld);

// round-trip
onlyOldOneof(fromHelpfulOneof(helpfulOneof(oldExample)));
onlyNewOneof(helpfulOneof(fromHelpfulOneof(newExample)));
1reaction
timostammcommented, Nov 17, 2022
Read more comments on GitHub >

github_iconTop Results From Across the Web

Language Guide (proto3) | Protocol Buffers - Google Developers
... Nested Types; Updating A Message Type; Unknown Fields; Any; Oneof; Maps; Packages ... In all cases, the value must fit in the...
Read more >
How to represent a mix of enum and oneof in protobuf
I am trying to create a protocol buffer message with fields that are either a message or one of a choice of some...
Read more >
oneOf / anyOf / allOf - react-jsonschema-form documentation
react-jsonschema-form supports custom widgets for oneOf, anyOf, and allOf. A schema with oneOf is valid if exactly one of the subschemas is valid....
Read more >
Understanding JSON Schema 2020-12 documentation
oneOf : (XOR) Must be valid against exactly one of the subschemas. All of these keywords must be set to an array, where...
Read more >
Update on the Cost and Quality of Defense Representation in ...
This report updates the 1998 Spencer Report. It includes revised commentary, endorsed by the Judicial Conference Committee on Defender Services, to the 1998 ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found