From 64ec41ef25568f6e0af94118fd5007d1360a8f44 Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Mon, 26 Feb 2024 15:34:47 -0500 Subject: [PATCH 1/2] Introduce type guard isMessage --- packages/protobuf/src/index.ts | 1 + packages/protobuf/src/is-message.ts | 56 +++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 packages/protobuf/src/is-message.ts diff --git a/packages/protobuf/src/index.ts b/packages/protobuf/src/index.ts index b50dee32d..a3fc5d004 100644 --- a/packages/protobuf/src/index.ts +++ b/packages/protobuf/src/index.ts @@ -22,6 +22,7 @@ export { codegenInfo } from "./codegen-info.js"; export { Message } from "./message.js"; export type { AnyMessage, PartialMessage, PlainMessage } from "./message.js"; +export { isMessage } from "./is-message.js"; export type { FieldInfo } from "./field.js"; export type { FieldList } from "./field-list.js"; diff --git a/packages/protobuf/src/is-message.ts b/packages/protobuf/src/is-message.ts new file mode 100644 index 000000000..5519d28fb --- /dev/null +++ b/packages/protobuf/src/is-message.ts @@ -0,0 +1,56 @@ +// Copyright 2021-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { MessageType } from "./message-type.js"; +import type { AnyMessage } from "./message.js"; +import { Message } from "./message.js"; + +/** + * Check whether the given object is an instance of the given message type. + * + * This function is equivalent to the `instanceof` operator. For example, + * `isMessage(foo, MyMessage)` is the same as `foo instanceof MyMessage`, and + * `isMessage(foo)` is the same as `foo instanceof Message`. + * + * Just like `instanceof`, `isMessage` narrows the type. The advantage of + * `isMessage` is that it compares identity by the message type name, not by + * class identity. This makes it robust against the dual package hazard and + * similar situations, where the same message is duplicated. + */ +export function isMessage = AnyMessage>( + arg: unknown, + type?: MessageType, +): arg is T { + if (arg === null || typeof arg != "object") { + return false; + } + if ( + !Object.getOwnPropertyNames(Message.prototype).every( + (m) => + m in arg && typeof (arg as Record)[m] == "function", + ) + ) { + return false; + } + const actualType = (arg as { getType(): unknown }).getType(); + if ( + actualType === null || + typeof actualType != "object" || + !("typeName" in actualType) || + typeof actualType.typeName != "string" + ) { + return false; + } + return type === undefined ? true : actualType.typeName == type.typeName; +} From 38cbfac57bc5528ea4f610ce37e92238a727e4af Mon Sep 17 00:00:00 2001 From: Timo Stamm Date: Mon, 26 Feb 2024 15:42:24 -0500 Subject: [PATCH 2/2] make --- packages/protobuf/src/is-message.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/protobuf/src/is-message.ts b/packages/protobuf/src/is-message.ts index 5519d28fb..49357ba44 100644 --- a/packages/protobuf/src/is-message.ts +++ b/packages/protobuf/src/is-message.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { MessageType } from "./message-type.js"; +import type { MessageType } from "./message-type.js"; import type { AnyMessage } from "./message.js"; import { Message } from "./message.js";