-
Notifications
You must be signed in to change notification settings - Fork 69
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
V2: export type MessageInit #941
Comments
Hey Nathan, we don't export We expect that you would typically want to write a generic function that works with arbitrary messages. We have an example in the v2 manual: import { getOption, type DescMessage, type MessageShape } from "@bufbuild/protobuf";
import { reflect } from "@bufbuild/protobuf/reflect";
import { sensitive } from "./gen/example-option_pb";
export function redact<Desc extends DescMessage>(
schema: Desc,
message: MessageShape<Desc>,
) {
const r = reflect(schema, message);
for (const field of r.fields) {
if (getOption(field, sensitive)) {
// This field has the option (example.options.sensitive) = true
r.clear(field);
}
}
} Tip
The function could also take an init object with But your function is a different case, it's specific to one message. I'd love to better understand the use case. What kind of function are you using it for? |
Hi Timo, Thanks for explanation. I'm not sure I totally understand your reasoning though. The message descriptor is really doing a lot because it also has the JSON type reference (when the Since filing this I turned on the JSON generated types because in my application, the Node.js backend-for-frontend has to communicate with the front-end by serializing via JSON. I had to turn it on because of That is some background on the application, but for the specific use case where I would use First scenario is where a UI component would normally be initialized with a message, but if that isn't available, or I'm manipulating the values for display (and pending UI), I want to just manipulate the relevant values without worrying about the export type IProtoGearRatioLimit = MessageInit<ProtoGearRatioLimit>;
export type RequiredNonNullable<T> = { [P in keyof T]-?: NonNullable<T[P]> };
export type GearRatioLimit = RequiredNonNullable<IProtoGearRatioLimit>;
const [gearLimits, setGearLimits] = useState<GearRatioLimit[]>([
{ gear: 1, highLimit: 207.1, lowLimit: 121.8 },
]); When I use
and
Second scenario is the return type of a bunch of my functions because I want to do a type union on whether a specific field is filled out or not. I have something like the following: export type IProtoRpcRequest = MessageInit<ProtoRpcRequest>;
interface RequiredRpcRequestRouting {
standId: number;
type: ProtoRpcRequestType;
}
export type RpcRequestWithRouting = IProtoRpcRequest &
RequiredRpcRequestRouting; Then functions that create the basics of a message: export function createDataCollectionStartManualRecordCommand(
standId: number
): RpcRequestWithRouting {
return {
standId,
timestamp: getTimestamp(),
type: ProtoRpcRequestType.DATA_COLLECTION_START_MANUAL_RECORD_COMMAND,
dataCollectionStartManualRecordCommand: {},
};
} Then finally, a function that needs a specific option if the specific fields to derive that information information are not filled out (or not available on that type): export interface RpcClientFunc {
(
request: RpcRequestWithRouting,
options?: RpcClientFuncOptions
): Promise<ProtoRpcResponse>;
(
request: IProtoRpcRequest,
options?: RpcClientFuncOptions & { rpcTopic: string }
): Promise<ProtoRpcResponse>;
} If I use
and
Note: I used |
It's awesome that TypeScript's type system is powerful enough to do things like that, but there are limits to the approach. Imagine that we need to change the behavior for specific fields in The solution is to generate an init type for such a message, and to attach it to the descriptor, so that it can be used in
I've pushed up your example in 4f2df57, but I can't reproduce the error. I wonder if your first scenario could be simplified? Messages are no longer classes with v2, and the goal is to make the generated type more versatile: import type { ProtoGearRatioLimit } from "./gen/ts/extra/issue941_pb.js";
const initialState: Omit<ProtoGearRatioLimit, "$typeName">[] = [
{
gear: 1,
highLimit: 2,
lowLimit: 3,
},
]; This doesn't require any mapped types except for the built-in I'm not sure I fully understand your second and third scenario, but it looks like you could straight-up use the generated type here as well? See dae4d47. Of course you'll need a mechanism to serialize the message for the BFF, but this approach should work well with proto3 messages (I understand that Do you aim to only use types in the frontend for small bundle sizes? |
Thanks for trying to replicate the issue in the tests. The weird thing is, that in my examples above, I had: export type IProtoRpcRequest = MessageInitShape<ProtoRpcRequestSchema>; I copied that right from my file and there was no TypeScript error on that line. I noticed your test had something different so I realized that I should have included the I would have expected TypeScript to complain about using a constant where it is expecting a type. When I was responding to you I quickly switched back to When adding For the second scenario, I initially did call The front-end is also directly serializing and de-serializing protobuf messages. I'm trying to add a gRPC service client to the back-end, so then there's the need to transport the resolved messages, so that's how the JSON serialization became a concern. Anyways, sorry about this chasing of errors. If the exporting of |
I'm stumped why TypeScript didn't provide a helpful error message 🤷
Yeah, in Protobuf, fields are basically always optional. It's an idiom that doesn't always match well with type systems with nullable types. We plan to provide some more options for use cases like this with protovalidate.
We'll better hold off in it for now 👍 Thanks for opening the issue! |
In V2, there is a type,
MessageInit
that excludes$typeName
(and$unknown
) and when passing objects to functions that will callcreate()
it is useful to use a derived type like that.Currently in V2, I have to do something like this:
MessageInitShape<typeof MyTypeSchema>
, but it would be shorter to do this:MessageInit<MyType>
.So this request is to export the type
MessageInit
so it can be imported.Also, having simpler types available might speed up the TypeScript language service, since it seems to have gotten fairly slow while I was trying out the V2 beta.
The text was updated successfully, but these errors were encountered: