Repository: stephenh/ts-proto
Pull request #870 (2023-07-09 14:54:49)

chore(deps): bump tough-cookie from 4.0.0 to 4.1.3 in /integration/simple-esmodule-interop
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.0.0 to 4.1.3.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/salesforce/tough-cookie/releases">tough-cookie's releases</a>.</em></p>
<blockquote>
<h2>4.1.3</h2>
<p>Security fix for Prototype Pollution discovery in <a href="https://redirect.github.com/salesforce/tough-cookie/issues/282">#282</a>. This is a minor release, although output from the <code>inspect</code> utility is affected by this change, we felt this change was important enough to be pushed into the next patch.</p>
<h2>4.1.2 -- Patch and Bugfix Release</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: allow set cookies with localhost by <a href="https://github.com/colincasey"><code>@​colincasey</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/253">salesforce/tough-cookie#253</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/salesforce/tough-cookie/compare/v4.1.1...v4.1.2">https://github.com/salesforce/tough-cookie/compare/v4.1.1...v4.1.2</a></p>
<h2>4.1.1</h2>
<h2>Patch Release</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: allow special use domains by default by <a href="https://github.com/colincasey"><code>@​colincasey</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/249">salesforce/tough-cookie#249</a></li>
<li>4.1.1 Patch -- allow special use domains by default by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/250">salesforce/tough-cookie#250</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/salesforce/tough-cookie/compare/v4.1.0...v4.1.1">https://github.com/salesforce/tough-cookie/compare/v4.1.0...v4.1.1</a></p>
<h2>4.1.0</h2>
<p>v4.1.0</p>
<p>Minor release, focused mainly on resolving reported issues and some minor feature work.</p>
<h2>What's Changed</h2>
<ul>
<li>Create CHANGELOG.md by <a href="https://github.com/ShivanKaul"><code>@​ShivanKaul</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/189">salesforce/tough-cookie#189</a></li>
<li>Missing param validation issue145 by <a href="https://github.com/medelibero-sfdc"><code>@​medelibero-sfdc</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/193">salesforce/tough-cookie#193</a></li>
<li>Create SECURITY.md by <a href="https://github.com/ShivanKaul"><code>@​ShivanKaul</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/201">salesforce/tough-cookie#201</a></li>
<li>Create CODE_OF_CONDUCT.md by <a href="https://github.com/ShivanKaul"><code>@​ShivanKaul</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/200">salesforce/tough-cookie#200</a></li>
<li>Fix for issue <a href="https://redirect.github.com/salesforce/tough-cookie/issues/195">#195</a> by <a href="https://github.com/medelibero-sfdc"><code>@​medelibero-sfdc</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/202">salesforce/tough-cookie#202</a></li>
<li>Add explanation and more special-use domains by <a href="https://github.com/ShivanKaul"><code>@​ShivanKaul</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/203">salesforce/tough-cookie#203</a></li>
<li>Sync of constructor options for serialization by <a href="https://github.com/medelibero-sfdc"><code>@​medelibero-sfdc</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/204">salesforce/tough-cookie#204</a></li>
<li>Returned null in case of empty cookie value by <a href="https://github.com/vsin12"><code>@​vsin12</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/196">salesforce/tough-cookie#196</a></li>
<li>132 str trim not a function by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/209">salesforce/tough-cookie#209</a></li>
<li>Fix for issue <a href="https://redirect.github.com/salesforce/tough-cookie/issues/153">#153</a> by <a href="https://github.com/medelibero-sfdc"><code>@​medelibero-sfdc</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/210">salesforce/tough-cookie#210</a></li>
<li>Fix permuteDomain with trailing dot by <a href="https://github.com/ruoho-sfdc"><code>@​ruoho-sfdc</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/216">salesforce/tough-cookie#216</a></li>
<li>Issue <a href="https://redirect.github.com/salesforce/tough-cookie/issues/213">#213</a> -- added gh-actions flow for building and testing tough-co… by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/218">salesforce/tough-cookie#218</a></li>
<li>Issue <a href="https://redirect.github.com/salesforce/tough-cookie/issues/210">#210</a> -- Updated workflow to use npm install. by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/220">salesforce/tough-cookie#220</a></li>
<li>@<a href="https://redirect.github.com/salesforce/tough-cookie/issues/215">GH-215</a> -- Tests that document localhost behavior when set as domain. by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/221">salesforce/tough-cookie#221</a></li>
<li>fix: MemoryCookieStore methods should exist on the prototype, not on the class. by <a href="https://github.com/wjhsf"><code>@​wjhsf</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/226">salesforce/tough-cookie#226</a></li>
<li>Unit test cases for <code>allowSpecialUseDomain</code> option by <a href="https://github.com/colincasey"><code>@​colincasey</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/225">salesforce/tough-cookie#225</a></li>
<li>[Snyk] Upgrade universalify from 0.1.2 to 0.2.0 by <a href="https://github.com/snyk-bot"><code>@​snyk-bot</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/228">salesforce/tough-cookie#228</a></li>
<li>React Native Support by <a href="https://github.com/colincasey"><code>@​colincasey</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/227">salesforce/tough-cookie#227</a></li>
<li>Adding Updating CODEOWNERS with ECCN as per Export Control Compliance by <a href="https://github.com/svc-scm"><code>@​svc-scm</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/223">salesforce/tough-cookie#223</a></li>
<li>fix: domain match routine by <a href="https://github.com/colincasey"><code>@​colincasey</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/236">salesforce/tough-cookie#236</a></li>
<li>Stop using the internal NodeJS punycode module by <a href="https://github.com/gboer"><code>@​gboer</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/238">salesforce/tough-cookie#238</a></li>
<li>Initial documentation review by <a href="https://github.com/mcarey86"><code>@​mcarey86</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/234">salesforce/tough-cookie#234</a></li>
<li>fix: distinguish between no samesite and samesite=none by <a href="https://github.com/colincasey"><code>@​colincasey</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/240">salesforce/tough-cookie#240</a></li>
<li>Prepare tough-cookie 4.1 for publishing (updated GitHub actions, move… by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/242">salesforce/tough-cookie#242</a></li>
<li>4.1.0 release to NPM by <a href="https://github.com/awaterma"><code>@​awaterma</code></a> in <a href="https://redirect.github.com/salesforce/tough-cookie/pull/245">salesforce/tough-cookie#245</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/salesforce/tough-cookie/commit/4ff4d29f6cefd279a412b8d62a21142ebd410b36"><code>4ff4d29</code></a> 4.1.3 release preparation, update the package and lib/version to 4.1.3. (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/284">#284</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/12d474791bb856004e858fdb1c47b7608d09cf6e"><code>12d4747</code></a> Prevent prototype pollution in cookie memstore (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/283">#283</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/f06b72d1d447f33dfa6222c0a3c0c5e063558248"><code>f06b72d</code></a> Fix documentation for store.findCookies, missing allowSpecialUseDomain proper...</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/b1a8898ee3f8af52c6c1c355555d9f50ebe626ce"><code>b1a8898</code></a> fix: allow set cookies with localhost (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/253">#253</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/ec707966e68a48199e646e2fa6b3055df6a280f0"><code>ec70796</code></a> 4.1.1 Patch -- allow special use domains by default (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/250">#250</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/d4ac5801dd2c2d53eec51329e5380bbffb23bfaf"><code>d4ac580</code></a> fix: allow special use domains by default (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/249">#249</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/79c2f7d373e39918605c270ecd965f507701233d"><code>79c2f7d</code></a> 4.1.0 release to NPM (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/245">#245</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/4fafc179a798a570e32fc698034f0480c07d9afa"><code>4fafc17</code></a> Prepare tough-cookie 4.1 for publishing (updated GitHub actions, move Dockerf...</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/aa4396da7abcb2dbe607db7b31606f7dd0f45709"><code>aa4396d</code></a> fix: distinguish between no samesite and samesite=none (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/240">#240</a>)</li>
<li><a href="https://github.com/salesforce/tough-cookie/commit/b8d751188da697157e5eed81fa1e5f806fdfb541"><code>b8d7511</code></a> Modernize README (<a href="https://redirect.github.com/salesforce/tough-cookie/issues/234">#234</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/salesforce/tough-cookie/compare/v4.0.0...v4.1.3">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tough-cookie&package-manager=npm_and_yarn&previous-version=4.0.0&new-version=4.1.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/stephenh/ts-proto/network/alerts).

</details>


Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index e698cee4..69f25a99 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,7 +12,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [14.x, 16.x, 18.x]
+        node-version: [16.x, 18.x, 20.x]
 
     steps:
       - uses: actions/checkout@v3
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 67377b7d..d2dde29d 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -12,7 +12,7 @@ jobs:
 
     strategy:
       matrix:
-        node-version: [14.x]
+        node-version: [20.x]
 
     steps:
       - uses: actions/checkout@v3
@@ -21,7 +21,16 @@ jobs:
         with:
           node-version: ${{ matrix.node-version }}
       - name: install dependencies
-        run: yarn install || echo "ignore failure"
+        run: yarn install
+      - name: Prepare Integration
+        run: ./integration/pbjs.sh
+      # This will fail if any git-tracked file has changed
+      - name: Codegen
+        run: yarn bin2ts
+      - name: TypeScript
+        run: yarn tsc:check
+        env:
+          CI: true
       - name: Prettier
         run: yarn format:check
         env:
diff --git a/integration/batching-with-context-esModuleInterop/batching.ts b/integration/batching-with-context-esModuleInterop/batching.ts
index b6a1f28e..eb760ceb 100644
--- a/integration/batching-with-context-esModuleInterop/batching.ts
+++ b/integration/batching-with-context-esModuleInterop/batching.ts
@@ -705,7 +705,7 @@ export class EntityServiceClientImpl<Context extends DataLoaders> implements Ent
       return new DataLoader<string, Entity>((ids) => {
         const request = { ids };
         return this.BatchMapQuery(ctx, request).then((res) => {
-          return ids.map((key) => res.entities[key]);
+          return ids.map((key) => res.entities[key] ?? fail());
         });
       }, { cacheKeyFn: hash, ...ctx.rpcDataLoaderOptions });
     });
@@ -770,3 +770,7 @@ function isObject(value: any): boolean {
 function isSet(value: any): boolean {
   return value !== null && value !== undefined;
 }
+
+function fail(message?: string): never {
+  throw new Error(message ?? "Failed");
+}
diff --git a/integration/batching-with-context/batching.ts b/integration/batching-with-context/batching.ts
index 6ea9a0cb..319eee66 100644
--- a/integration/batching-with-context/batching.ts
+++ b/integration/batching-with-context/batching.ts
@@ -705,7 +705,7 @@ export class EntityServiceClientImpl<Context extends DataLoaders> implements Ent
       return new DataLoader<string, Entity>((ids) => {
         const request = { ids };
         return this.BatchMapQuery(ctx, request).then((res) => {
-          return ids.map((key) => res.entities[key]);
+          return ids.map((key) => res.entities[key] ?? fail());
         });
       }, { cacheKeyFn: hash, ...ctx.rpcDataLoaderOptions });
     });
@@ -770,3 +770,7 @@ function isObject(value: any): boolean {
 function isSet(value: any): boolean {
   return value !== null && value !== undefined;
 }
+
+function fail(message?: string): never {
+  throw new Error(message ?? "Failed");
+}
diff --git a/integration/bytes-as-base64/message.ts b/integration/bytes-as-base64/message.ts
index 3b296488..50a0b580 100644
--- a/integration/bytes-as-base64/message.ts
+++ b/integration/bytes-as-base64/message.ts
@@ -33,10 +33,10 @@ export const Message = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/bytes-node/google/protobuf/wrappers.ts b/integration/bytes-node/google/protobuf/wrappers.ts
index 61fcfe42..6e5cfb4b 100644
--- a/integration/bytes-node/google/protobuf/wrappers.ts
+++ b/integration/bytes-node/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/bytes-node/point.ts b/integration/bytes-node/point.ts
index 12aef40c..66421dcf 100644
--- a/integration/bytes-node/point.ts
+++ b/integration/bytes-node/point.ts
@@ -81,10 +81,10 @@ export const Point = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/file-suffix/google/protobuf/timestamp.pb.ts b/integration/file-suffix/google/protobuf/timestamp.pb.ts
index e4b5e083..dc3946fe 100644
--- a/integration/file-suffix/google/protobuf/timestamp.pb.ts
+++ b/integration/file-suffix/google/protobuf/timestamp.pb.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/generic-metadata/some-file.ts b/integration/generic-metadata/some-file.ts
new file mode 100644
index 00000000..2921688a
--- /dev/null
+++ b/integration/generic-metadata/some-file.ts
@@ -0,0 +1,3 @@
+export interface Foo {
+  foo: "bar";
+}
diff --git a/integration/grpc-js-use-date-false/google/protobuf/timestamp.ts b/integration/grpc-js-use-date-false/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/grpc-js-use-date-false/google/protobuf/timestamp.ts
+++ b/integration/grpc-js-use-date-false/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-js-use-date-string/google/protobuf/timestamp.ts b/integration/grpc-js-use-date-string/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/grpc-js-use-date-string/google/protobuf/timestamp.ts
+++ b/integration/grpc-js-use-date-string/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-js-use-date-true/google/protobuf/timestamp.ts b/integration/grpc-js-use-date-true/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/grpc-js-use-date-true/google/protobuf/timestamp.ts
+++ b/integration/grpc-js-use-date-true/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-js/google/protobuf/timestamp.ts b/integration/grpc-js/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/grpc-js/google/protobuf/timestamp.ts
+++ b/integration/grpc-js/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-js/google/protobuf/wrappers.ts b/integration/grpc-js/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/grpc-js/google/protobuf/wrappers.ts
+++ b/integration/grpc-js/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-web-abort-signal/example.ts b/integration/grpc-web-abort-signal/example.ts
index 6298c146..66bdb755 100644
--- a/integration/grpc-web-abort-signal/example.ts
+++ b/integration/grpc-web-abort-signal/example.ts
@@ -994,14 +994,14 @@ export class GrpcWebImpl {
     const request = { ..._request, ...methodDesc.requestType };
     const maybeCombinedMetadata = metadata && this.options.metadata
       ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-      : metadata || this.options.metadata;
+      : metadata ?? this.options.metadata;
     return new Promise((resolve, reject) => {
       const client = grpc.unary(methodDesc, {
         request,
         host: this.host,
-        metadata: maybeCombinedMetadata,
-        transport: this.options.transport,
-        debug: this.options.debug,
+        metadata: maybeCombinedMetadata ?? {},
+        ...(this.options.transport !== undefined ? { transport: this.options.transport } : {}),
+        debug: this.options.debug ?? false,
         onEnd: function (response) {
           if (response.status === grpc.Code.OK) {
             resolve(response.message!.toObject());
@@ -1027,20 +1027,21 @@ export class GrpcWebImpl {
     metadata: grpc.Metadata | undefined,
     abortSignal?: AbortSignal,
   ): Observable<any> {
-    const upStreamCodes = this.options.upStreamRetryCodes || [];
+    const upStreamCodes = this.options.upStreamRetryCodes ?? [];
     const DEFAULT_TIMEOUT_TIME: number = 3_000;
     const request = { ..._request, ...methodDesc.requestType };
+    const transport = this.options.streamingTransport ?? this.options.transport;
     const maybeCombinedMetadata = metadata && this.options.metadata
       ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-      : metadata || this.options.metadata;
+      : metadata ?? this.options.metadata;
     return new Observable((observer) => {
       const upStream = (() => {
         const client = grpc.invoke(methodDesc, {
           host: this.host,
           request,
-          transport: this.options.streamingTransport || this.options.transport,
-          metadata: maybeCombinedMetadata,
-          debug: this.options.debug,
+          ...(transport !== undefined ? { transport } : {}),
+          metadata: maybeCombinedMetadata ?? {},
+          debug: this.options.debug ?? false,
           onMessage: (next) => observer.next(next),
           onEnd: (code: grpc.Code, message: string, trailers: grpc.Metadata) => {
             if (code === 0) {
@@ -1073,10 +1074,10 @@ export class GrpcWebImpl {
   }
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-web-no-streaming-observable/example.ts b/integration/grpc-web-no-streaming-observable/example.ts
index 1a6d76f3..76d2ddf8 100644
--- a/integration/grpc-web-no-streaming-observable/example.ts
+++ b/integration/grpc-web-no-streaming-observable/example.ts
@@ -441,14 +441,14 @@ export class GrpcWebImpl {
     const request = { ..._request, ...methodDesc.requestType };
     const maybeCombinedMetadata = metadata && this.options.metadata
       ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-      : metadata || this.options.metadata;
+      : metadata ?? this.options.metadata;
     return new Observable((observer) => {
       grpc.unary(methodDesc, {
         request,
         host: this.host,
-        metadata: maybeCombinedMetadata,
-        transport: this.options.transport,
-        debug: this.options.debug,
+        metadata: maybeCombinedMetadata ?? {},
+        ...(this.options.transport !== undefined ? { transport: this.options.transport } : {}),
+        debug: this.options.debug ?? false,
         onEnd: (next) => {
           if (next.status !== 0) {
             const err = new GrpcWebError(next.statusMessage, next.status, next.trailers);
@@ -463,10 +463,10 @@ export class GrpcWebImpl {
   }
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-web-no-streaming/example.ts b/integration/grpc-web-no-streaming/example.ts
index 528aa702..205d0a72 100644
--- a/integration/grpc-web-no-streaming/example.ts
+++ b/integration/grpc-web-no-streaming/example.ts
@@ -439,14 +439,14 @@ export class GrpcWebImpl {
     const request = { ..._request, ...methodDesc.requestType };
     const maybeCombinedMetadata = metadata && this.options.metadata
       ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-      : metadata || this.options.metadata;
+      : metadata ?? this.options.metadata;
     return new Promise((resolve, reject) => {
       grpc.unary(methodDesc, {
         request,
         host: this.host,
-        metadata: maybeCombinedMetadata,
-        transport: this.options.transport,
-        debug: this.options.debug,
+        metadata: maybeCombinedMetadata ?? {},
+        ...(this.options.transport !== undefined ? { transport: this.options.transport } : {}),
+        debug: this.options.debug ?? false,
         onEnd: function (response) {
           if (response.status === grpc.Code.OK) {
             resolve(response.message!.toObject());
@@ -460,10 +460,10 @@ export class GrpcWebImpl {
   }
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/grpc-web/client-ts.ts b/integration/grpc-web/client-ts.ts
index 5781306c..bc3c1da1 100755
--- a/integration/grpc-web/client-ts.ts
+++ b/integration/grpc-web/client-ts.ts
@@ -31,7 +31,7 @@ async function main() {
   try {
     await creds.Update({ description: 'test desc2' });
   } catch (e) {
-    console.log('got expected error', e.message);
+    console.log('got expected error', e);
   }
 
   const obs = client.ActiveUserSettingsStream({});
diff --git a/integration/grpc-web/example.ts b/integration/grpc-web/example.ts
index 5d52d6f5..91406139 100644
--- a/integration/grpc-web/example.ts
+++ b/integration/grpc-web/example.ts
@@ -949,14 +949,14 @@ export class GrpcWebImpl {
     const request = { ..._request, ...methodDesc.requestType };
     const maybeCombinedMetadata = metadata && this.options.metadata
       ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-      : metadata || this.options.metadata;
+      : metadata ?? this.options.metadata;
     return new Promise((resolve, reject) => {
       grpc.unary(methodDesc, {
         request,
         host: this.host,
-        metadata: maybeCombinedMetadata,
-        transport: this.options.transport,
-        debug: this.options.debug,
+        metadata: maybeCombinedMetadata ?? {},
+        ...(this.options.transport !== undefined ? { transport: this.options.transport } : {}),
+        debug: this.options.debug ?? false,
         onEnd: function (response) {
           if (response.status === grpc.Code.OK) {
             resolve(response.message!.toObject());
@@ -974,20 +974,21 @@ export class GrpcWebImpl {
     _request: any,
     metadata: grpc.Metadata | undefined,
   ): Observable<any> {
-    const upStreamCodes = this.options.upStreamRetryCodes || [];
+    const upStreamCodes = this.options.upStreamRetryCodes ?? [];
     const DEFAULT_TIMEOUT_TIME: number = 3_000;
     const request = { ..._request, ...methodDesc.requestType };
+    const transport = this.options.streamingTransport ?? this.options.transport;
     const maybeCombinedMetadata = metadata && this.options.metadata
       ? new BrowserHeaders({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-      : metadata || this.options.metadata;
+      : metadata ?? this.options.metadata;
     return new Observable((observer) => {
       const upStream = (() => {
         const client = grpc.invoke(methodDesc, {
           host: this.host,
           request,
-          transport: this.options.streamingTransport || this.options.transport,
-          metadata: maybeCombinedMetadata,
-          debug: this.options.debug,
+          ...(transport !== undefined ? { transport } : {}),
+          metadata: maybeCombinedMetadata ?? {},
+          debug: this.options.debug ?? false,
           onMessage: (next) => observer.next(next),
           onEnd: (code: grpc.Code, message: string, trailers: grpc.Metadata) => {
             if (code === 0) {
@@ -1011,10 +1012,10 @@ export class GrpcWebImpl {
   }
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/import-mapping/google/protobuf/timestamp.ts b/integration/import-mapping/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/import-mapping/google/protobuf/timestamp.ts
+++ b/integration/import-mapping/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/import-suffix/google/protobuf/timestamp.pb.ts b/integration/import-suffix/google/protobuf/timestamp.pb.ts
index cc89db58..64e5e644 100644
--- a/integration/import-suffix/google/protobuf/timestamp.pb.ts
+++ b/integration/import-suffix/google/protobuf/timestamp.pb.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/meta-typings/google/protobuf/timestamp.ts b/integration/meta-typings/google/protobuf/timestamp.ts
index 0bb6fcf2..476e3768 100644
--- a/integration/meta-typings/google/protobuf/timestamp.ts
+++ b/integration/meta-typings/google/protobuf/timestamp.ts
@@ -278,10 +278,10 @@ export const protoMetadata: ProtoMetadata = {
   dependencies: [],
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/meta-typings/google/protobuf/wrappers.ts b/integration/meta-typings/google/protobuf/wrappers.ts
index b2d0aeb3..21c5791a 100644
--- a/integration/meta-typings/google/protobuf/wrappers.ts
+++ b/integration/meta-typings/google/protobuf/wrappers.ts
@@ -817,10 +817,10 @@ export const protoMetadata: ProtoMetadata = {
   dependencies: [],
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/meta-typings/simple.ts b/integration/meta-typings/simple.ts
index 5602bb82..8531912c 100644
--- a/integration/meta-typings/simple.ts
+++ b/integration/meta-typings/simple.ts
@@ -2950,10 +2950,10 @@ export const protoMetadata: ProtoMetadata = {
   dependencies: [protoMetadata1, protoMetadata2, protoMetadata3, protoMetadata4],
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/nice-grpc/google/protobuf/timestamp.ts b/integration/nice-grpc/google/protobuf/timestamp.ts
index bbf01134..4dea0b96 100644
--- a/integration/nice-grpc/google/protobuf/timestamp.ts
+++ b/integration/nice-grpc/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/nice-grpc/google/protobuf/wrappers.ts b/integration/nice-grpc/google/protobuf/wrappers.ts
index 1387d5f3..c1ea99fa 100644
--- a/integration/nice-grpc/google/protobuf/wrappers.ts
+++ b/integration/nice-grpc/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/oneof-properties/oneof.ts b/integration/oneof-properties/oneof.ts
index 9ad527f5..32683549 100644
--- a/integration/oneof-properties/oneof.ts
+++ b/integration/oneof-properties/oneof.ts
@@ -337,10 +337,10 @@ export const PleaseChoose_Submessage = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/oneof-unions-snake/google/protobuf/struct.ts b/integration/oneof-unions-snake/google/protobuf/struct.ts
index a2164b32..0c168958 100644
--- a/integration/oneof-unions-snake/google/protobuf/struct.ts
+++ b/integration/oneof-unions-snake/google/protobuf/struct.ts
@@ -72,7 +72,8 @@ export interface Value {
     | { $case: "string_value"; string_value: string }
     | { $case: "bool_value"; bool_value: boolean }
     | { $case: "struct_value"; struct_value: { [key: string]: any } | undefined }
-    | { $case: "list_value"; list_value: Array<any> | undefined };
+    | { $case: "list_value"; list_value: Array<any> | undefined }
+    | undefined;
 }
 
 /**
diff --git a/integration/oneof-unions/google/protobuf/struct.ts b/integration/oneof-unions/google/protobuf/struct.ts
index 1f50b37e..3776f763 100644
--- a/integration/oneof-unions/google/protobuf/struct.ts
+++ b/integration/oneof-unions/google/protobuf/struct.ts
@@ -72,7 +72,8 @@ export interface Value {
     | { $case: "stringValue"; stringValue: string }
     | { $case: "boolValue"; boolValue: boolean }
     | { $case: "structValue"; structValue: { [key: string]: any } | undefined }
-    | { $case: "listValue"; listValue: Array<any> | undefined };
+    | { $case: "listValue"; listValue: Array<any> | undefined }
+    | undefined;
 }
 
 /**
diff --git a/integration/oneof-unions/oneof.ts b/integration/oneof-unions/oneof.ts
index ef44f16d..10f9a8c7 100644
--- a/integration/oneof-unions/oneof.ts
+++ b/integration/oneof-unions/oneof.ts
@@ -12,12 +12,13 @@ export interface PleaseChoose {
     | { $case: "aMessage"; aMessage: PleaseChoose_Submessage }
     | { $case: "aBool"; aBool: boolean }
     | { $case: "bunchaBytes"; bunchaBytes: Uint8Array }
-    | { $case: "anEnum"; anEnum: PleaseChoose_StateEnum };
+    | { $case: "anEnum"; anEnum: PleaseChoose_StateEnum }
+    | undefined;
   age: number;
   eitherOr?: { $case: "either"; either: string } | { $case: "or"; or: string } | {
     $case: "thirdOption";
     thirdOption: string;
-  };
+  } | undefined;
   signature: Uint8Array;
   value: any | undefined;
 }
@@ -463,10 +464,10 @@ export const SimpleButOptional = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/options/google/protobuf/descriptor.ts b/integration/options/google/protobuf/descriptor.ts
index 039e186a..112bb593 100644
--- a/integration/options/google/protobuf/descriptor.ts
+++ b/integration/options/google/protobuf/descriptor.ts
@@ -5782,10 +5782,10 @@ export const protoMetadata: ProtoMetadata = {
   dependencies: [],
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/output-decode-only/google/protobuf/wrappers.ts b/integration/output-decode-only/google/protobuf/wrappers.ts
index 0cb3d044..d05f90e8 100644
--- a/integration/output-decode-only/google/protobuf/wrappers.ts
+++ b/integration/output-decode-only/google/protobuf/wrappers.ts
@@ -355,10 +355,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/remove-enum-prefix/remove-enum-prefix.ts b/integration/remove-enum-prefix/remove-enum-prefix.ts
index 5542cf29..c243421b 100644
--- a/integration/remove-enum-prefix/remove-enum-prefix.ts
+++ b/integration/remove-enum-prefix/remove-enum-prefix.ts
@@ -72,10 +72,10 @@ export function barToJSON(object: Bar): string {
   }
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-esmodule-interop/simple.ts b/integration/simple-esmodule-interop/simple.ts
index 6a8f5b13..422d6ec0 100644
--- a/integration/simple-esmodule-interop/simple.ts
+++ b/integration/simple-esmodule-interop/simple.ts
@@ -306,10 +306,10 @@ export const Numbers = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-esmodule-interop/yarn.lock b/integration/simple-esmodule-interop/yarn.lock
index 63c4d9d7..994b86b1 100644
--- a/integration/simple-esmodule-interop/yarn.lock
+++ b/integration/simple-esmodule-interop/yarn.lock
@@ -4021,6 +4021,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"querystringify@npm:^2.1.1":
+  version: 2.2.0
+  resolution: "querystringify@npm:2.2.0"
+  checksum: 5641ea231bad7ef6d64d9998faca95611ed4b11c2591a8cae741e178a974f6a8e0ebde008475259abe1621cb15e692404e6b6626e927f7b849d5c09392604b15
+  languageName: node
+  linkType: hard
+
 "react-is@npm:^17.0.1":
   version: 17.0.2
   resolution: "react-is@npm:17.0.2"
@@ -4107,6 +4114,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"requires-port@npm:^1.0.0":
+  version: 1.0.0
+  resolution: "requires-port@npm:1.0.0"
+  checksum: eee0e303adffb69be55d1a214e415cf42b7441ae858c76dfc5353148644f6fd6e698926fc4643f510d5c126d12a705e7c8ed7e38061113bdf37547ab356797ff
+  languageName: node
+  linkType: hard
+
 "resolve-cwd@npm:^3.0.0":
   version: 3.0.0
   resolution: "resolve-cwd@npm:3.0.0"
@@ -4766,13 +4780,14 @@ __metadata:
   linkType: hard
 
 "tough-cookie@npm:^4.0.0":
-  version: 4.0.0
-  resolution: "tough-cookie@npm:4.0.0"
+  version: 4.1.3
+  resolution: "tough-cookie@npm:4.1.3"
   dependencies:
     psl: ^1.1.33
     punycode: ^2.1.1
-    universalify: ^0.1.2
-  checksum: 0891b37eb7d17faa3479d47f0dce2e3007f2583094ad272f2670d120fbcc3df3b0b0a631ba96ecad49f9e2297d93ff8995ce0d3292d08dd7eabe162f5b224d69
+    universalify: ^0.2.0
+    url-parse: ^1.5.3
+  checksum: c9226afff36492a52118432611af083d1d8493a53ff41ec4ea48e5b583aec744b989e4280bcf476c910ec1525a89a4a0f1cae81c08b18fb2ec3a9b3a72b91dcc
   languageName: node
   linkType: hard
 
@@ -4881,10 +4896,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"universalify@npm:^0.1.2":
-  version: 0.1.2
-  resolution: "universalify@npm:0.1.2"
-  checksum: 40cdc60f6e61070fe658ca36016a8f4ec216b29bf04a55dce14e3710cc84c7448538ef4dad3728d0bfe29975ccd7bfb5f414c45e7b78883567fb31b246f02dff
+"universalify@npm:^0.2.0":
+  version: 0.2.0
+  resolution: "universalify@npm:0.2.0"
+  checksum: e86134cb12919d177c2353196a4cc09981524ee87abf621f7bc8d249dbbbebaec5e7d1314b96061497981350df786e4c5128dbf442eba104d6e765bc260678b5
   languageName: node
   linkType: hard
 
@@ -4905,6 +4920,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"url-parse@npm:^1.5.3":
+  version: 1.5.10
+  resolution: "url-parse@npm:1.5.10"
+  dependencies:
+    querystringify: ^2.1.1
+    requires-port: ^1.0.0
+  checksum: fbdba6b1d83336aca2216bbdc38ba658d9cfb8fc7f665eb8b17852de638ff7d1a162c198a8e4ed66001ddbf6c9888d41e4798912c62b4fd777a31657989f7bdf
+  languageName: node
+  linkType: hard
+
 "use@npm:^3.1.0":
   version: 3.1.1
   resolution: "use@npm:3.1.1"
diff --git a/integration/simple-json-name/google/protobuf/timestamp.ts b/integration/simple-json-name/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/simple-json-name/google/protobuf/timestamp.ts
+++ b/integration/simple-json-name/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-long-bigint/google/protobuf/wrappers.ts b/integration/simple-long-bigint/google/protobuf/wrappers.ts
index f493e2de..613a75aa 100644
--- a/integration/simple-long-bigint/google/protobuf/wrappers.ts
+++ b/integration/simple-long-bigint/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-long-string/google/protobuf/wrappers.ts b/integration/simple-long-string/google/protobuf/wrappers.ts
index 42e990ef..a69f21c2 100644
--- a/integration/simple-long-string/google/protobuf/wrappers.ts
+++ b/integration/simple-long-string/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-long/google/protobuf/wrappers.ts b/integration/simple-long/google/protobuf/wrappers.ts
index 9269f087..3347b40d 100644
--- a/integration/simple-long/google/protobuf/wrappers.ts
+++ b/integration/simple-long/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-optionals/google/protobuf/timestamp.ts b/integration/simple-optionals/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/simple-optionals/google/protobuf/timestamp.ts
+++ b/integration/simple-optionals/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-optionals/google/protobuf/wrappers.ts b/integration/simple-optionals/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/simple-optionals/google/protobuf/wrappers.ts
+++ b/integration/simple-optionals/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-optionals/import_dir/thing.ts b/integration/simple-optionals/import_dir/thing.ts
index 34d8a553..231c9e09 100644
--- a/integration/simple-optionals/import_dir/thing.ts
+++ b/integration/simple-optionals/import_dir/thing.ts
@@ -5,7 +5,7 @@ import { Timestamp } from "../google/protobuf/timestamp";
 export const protobufPackage = "simple";
 
 export interface ImportedThing {
-  createdAt?: Date;
+  createdAt?: Date | undefined;
 }
 
 function createBaseImportedThing(): ImportedThing {
diff --git a/integration/simple-optionals/simple.ts b/integration/simple-optionals/simple.ts
index edf6bf89..4a3224c7 100644
--- a/integration/simple-optionals/simple.ts
+++ b/integration/simple-optionals/simple.ts
@@ -58,15 +58,15 @@ export interface Simple {
   /** Age */
   age: number;
   /** This comment will also attach */
-  createdAt?: Date;
-  child?: Child;
+  createdAt?: Date | undefined;
+  child?: Child | undefined;
   state: StateEnum;
   grandChildren: Child[];
   coins: number[];
   snacks: string[];
   oldStates: StateEnum[];
   /** A thing (imported from thing) */
-  thing?: ImportedThing;
+  thing?: ImportedThing | undefined;
 }
 
 export interface Child {
@@ -115,7 +115,7 @@ export function child_TypeToJSON(object: Child_Type): string {
 
 export interface Nested {
   name: string;
-  message?: Nested_InnerMessage;
+  message?: Nested_InnerMessage | undefined;
   state: Nested_InnerEnum;
 }
 
@@ -161,7 +161,7 @@ export function nested_InnerEnumToJSON(object: Nested_InnerEnum): string {
 /** Comment for a nested message * / */
 export interface Nested_InnerMessage {
   name: string;
-  deep?: Nested_InnerMessage_DeepMessage;
+  deep?: Nested_InnerMessage_DeepMessage | undefined;
 }
 
 export interface Nested_InnerMessage_DeepMessage {
@@ -174,9 +174,9 @@ export interface OneOfMessage {
 }
 
 export interface SimpleWithWrappers {
-  name?: string;
-  age?: number;
-  enabled?: boolean;
+  name?: string | undefined;
+  age?: number | undefined;
+  enabled?: boolean | undefined;
   coins: number[];
   snacks: string[];
 }
@@ -193,7 +193,7 @@ export interface SimpleWithMap {
 
 export interface SimpleWithMap_EntitiesByIdEntry {
   key: number;
-  value?: Entity;
+  value?: Entity | undefined;
 }
 
 export interface SimpleWithMap_NameLookupEntry {
@@ -212,7 +212,7 @@ export interface SimpleWithSnakeCaseMap {
 
 export interface SimpleWithSnakeCaseMap_EntitiesByIdEntry {
   key: number;
-  value?: Entity;
+  value?: Entity | undefined;
 }
 
 export interface PingRequest {
@@ -1869,10 +1869,10 @@ interface Rpc {
   request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-optionals/thing.ts b/integration/simple-optionals/thing.ts
index 28773c45..e8529c1f 100644
--- a/integration/simple-optionals/thing.ts
+++ b/integration/simple-optionals/thing.ts
@@ -5,7 +5,7 @@ import { Timestamp } from "./google/protobuf/timestamp";
 export const protobufPackage = "simple";
 
 export interface ImportedThing {
-  createdAt?: Date;
+  createdAt?: Date | undefined;
 }
 
 function createBaseImportedThing(): ImportedThing {
diff --git a/integration/simple-prototype-defaults/google/protobuf/timestamp.ts b/integration/simple-prototype-defaults/google/protobuf/timestamp.ts
index b1ca467d..e056b52e 100644
--- a/integration/simple-prototype-defaults/google/protobuf/timestamp.ts
+++ b/integration/simple-prototype-defaults/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-prototype-defaults/google/protobuf/wrappers.ts b/integration/simple-prototype-defaults/google/protobuf/wrappers.ts
index 83312667..7c1dd071 100644
--- a/integration/simple-prototype-defaults/google/protobuf/wrappers.ts
+++ b/integration/simple-prototype-defaults/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-prototype-defaults/simple.ts b/integration/simple-prototype-defaults/simple.ts
index 48504179..7963712f 100644
--- a/integration/simple-prototype-defaults/simple.ts
+++ b/integration/simple-prototype-defaults/simple.ts
@@ -2796,10 +2796,10 @@ interface Rpc {
   request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-snake/google/protobuf/timestamp.ts b/integration/simple-snake/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/simple-snake/google/protobuf/timestamp.ts
+++ b/integration/simple-snake/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-snake/google/protobuf/wrappers.ts b/integration/simple-snake/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/simple-snake/google/protobuf/wrappers.ts
+++ b/integration/simple-snake/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-snake/simple.ts b/integration/simple-snake/simple.ts
index ac4c2252..f8135112 100644
--- a/integration/simple-snake/simple.ts
+++ b/integration/simple-snake/simple.ts
@@ -1930,10 +1930,10 @@ interface Rpc {
   request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-unrecognized-enum/google/protobuf/timestamp.ts b/integration/simple-unrecognized-enum/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/simple-unrecognized-enum/google/protobuf/timestamp.ts
+++ b/integration/simple-unrecognized-enum/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-unrecognized-enum/google/protobuf/wrappers.ts b/integration/simple-unrecognized-enum/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/simple-unrecognized-enum/google/protobuf/wrappers.ts
+++ b/integration/simple-unrecognized-enum/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple-unrecognized-enum/simple.ts b/integration/simple-unrecognized-enum/simple.ts
index fb4ff849..bf8f3c9e 100644
--- a/integration/simple-unrecognized-enum/simple.ts
+++ b/integration/simple-unrecognized-enum/simple.ts
@@ -1857,10 +1857,10 @@ interface Rpc {
   request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple/google/protobuf/timestamp.ts b/integration/simple/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/simple/google/protobuf/timestamp.ts
+++ b/integration/simple/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple/google/protobuf/wrappers.ts b/integration/simple/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/simple/google/protobuf/wrappers.ts
+++ b/integration/simple/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/simple/simple.ts b/integration/simple/simple.ts
index ed5fa6b3..3a599c33 100644
--- a/integration/simple/simple.ts
+++ b/integration/simple/simple.ts
@@ -2799,10 +2799,10 @@ interface Rpc {
   request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
 }
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/static-only-type-registry/google/protobuf/timestamp.ts b/integration/static-only-type-registry/google/protobuf/timestamp.ts
index 1630303b..ac2ec05f 100644
--- a/integration/static-only-type-registry/google/protobuf/timestamp.ts
+++ b/integration/static-only-type-registry/google/protobuf/timestamp.ts
@@ -187,10 +187,10 @@ export const Timestamp = {
 
 messageTypeRegistry.set(Timestamp.$type, Timestamp);
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/static-only/google/protobuf/timestamp.ts b/integration/static-only/google/protobuf/timestamp.ts
index 59fdfd02..1095ff43 100644
--- a/integration/static-only/google/protobuf/timestamp.ts
+++ b/integration/static-only/google/protobuf/timestamp.ts
@@ -184,10 +184,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/tsconfig.json b/integration/tsconfig.json
index 118aad9a..8e5407fb 100644
--- a/integration/tsconfig.json
+++ b/integration/tsconfig.json
@@ -1,12 +1,15 @@
 {
+  "extends": "../tsconfig.json",
   "compilerOptions": {
-    "target": "es2018",
-    "lib": ["es2018"],
-    "module": "commonjs",
-    "strict": true,
-    "outDir": "build",
-    "skipLibCheck": true,
-    "experimentalDecorators": true
+    "lib": ["es2018", "DOM"],
   },
-  "include": ["./"]
-}
+  "include": [
+    "**/*-test.ts"
+  ],
+  "exclude": [
+    "angular",
+    "batching-with-context-esModuleInterop",
+    "import-mapping/mapping.ts",
+    "simple-esmodule-interop"
+  ]
+}
\ No newline at end of file
diff --git a/integration/tsconfig.proto.json b/integration/tsconfig.proto.json
new file mode 100644
index 00000000..842413ac
--- /dev/null
+++ b/integration/tsconfig.proto.json
@@ -0,0 +1,20 @@
+{
+  "extends": ["../tsconfig.json", "@tsconfig/strictest/tsconfig.json"],
+  "compilerOptions": {
+    "lib": ["es2018", "DOM"],
+    "esModuleInterop": false,
+    "noUnusedParameters": false,
+    "noUnusedLocals": false
+  },
+  "include": [
+    "**/*.ts"
+  ],
+  "exclude": [
+    "**/*-test.ts",
+    "codegen.ts",
+    "angular",
+    "batching-with-context-esModuleInterop",
+    "import-mapping/mapping.ts",
+    "simple-esmodule-interop"
+  ]
+}
\ No newline at end of file
diff --git a/integration/type-annotations/google/protobuf/timestamp.ts b/integration/type-annotations/google/protobuf/timestamp.ts
index ebe4a8b4..8dcb63d9 100644
--- a/integration/type-annotations/google/protobuf/timestamp.ts
+++ b/integration/type-annotations/google/protobuf/timestamp.ts
@@ -186,10 +186,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/type-registry/google/protobuf/timestamp.ts b/integration/type-registry/google/protobuf/timestamp.ts
index 9f31d731..b97b50a4 100644
--- a/integration/type-registry/google/protobuf/timestamp.ts
+++ b/integration/type-registry/google/protobuf/timestamp.ts
@@ -189,10 +189,10 @@ export const Timestamp = {
 
 messageTypeRegistry.set(Timestamp.$type, Timestamp);
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/unknown-fields/google/protobuf/compiler/plugin.ts b/integration/unknown-fields/google/protobuf/compiler/plugin.ts
index d66e8bb2..38071a47 100644
--- a/integration/unknown-fields/google/protobuf/compiler/plugin.ts
+++ b/integration/unknown-fields/google/protobuf/compiler/plugin.ts
@@ -15,7 +15,7 @@ export interface Version {
    * be empty for mainline stable releases.
    */
   suffix: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** An encoded CodeGeneratorRequest is written to the plugin's stdin. */
@@ -47,7 +47,7 @@ export interface CodeGeneratorRequest {
   protoFile: FileDescriptorProto[];
   /** The version number of protocol compiler. */
   compilerVersion: Version | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** The plugin writes an encoded CodeGeneratorResponse to stdout. */
@@ -69,7 +69,7 @@ export interface CodeGeneratorResponse {
    */
   supportedFeatures: number;
   file: CodeGeneratorResponse_File[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Sync with code_generator.h. */
@@ -143,7 +143,7 @@ export interface CodeGeneratorResponse_File {
    * into the code generation metadata for the generated files.
    */
   generatedCodeInfo: GeneratedCodeInfo | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseVersion(): Version {
@@ -165,8 +165,7 @@ export const Version = {
       writer.uint32(34).string(message.suffix);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -255,8 +254,7 @@ export const CodeGeneratorRequest = {
       Version.encode(message.compilerVersion, writer.uint32(26).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -342,8 +340,7 @@ export const CodeGeneratorResponse = {
       CodeGeneratorResponse_File.encode(v!, writer.uint32(122).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -425,8 +422,7 @@ export const CodeGeneratorResponse_File = {
       GeneratedCodeInfo.encode(message.generatedCodeInfo, writer.uint32(130).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -496,10 +492,10 @@ export const CodeGeneratorResponse_File = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/unknown-fields/google/protobuf/descriptor.ts b/integration/unknown-fields/google/protobuf/descriptor.ts
index 3364f521..835ca15e 100644
--- a/integration/unknown-fields/google/protobuf/descriptor.ts
+++ b/integration/unknown-fields/google/protobuf/descriptor.ts
@@ -10,7 +10,7 @@ export const protobufPackage = "google.protobuf";
  */
 export interface FileDescriptorSet {
   file: FileDescriptorProto[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes a complete .proto file. */
@@ -50,7 +50,7 @@ export interface FileDescriptorProto {
    * The supported values are "proto2" and "proto3".
    */
   syntax: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes a message type. */
@@ -69,7 +69,7 @@ export interface DescriptorProto {
    * A given name may only be reserved once.
    */
   reservedName: string[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface DescriptorProto_ExtensionRange {
@@ -78,7 +78,7 @@ export interface DescriptorProto_ExtensionRange {
   /** Exclusive. */
   end: number;
   options: ExtensionRangeOptions | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /**
@@ -91,13 +91,13 @@ export interface DescriptorProto_ReservedRange {
   start: number;
   /** Exclusive. */
   end: number;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface ExtensionRangeOptions {
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes a field within a message. */
@@ -170,7 +170,7 @@ export interface FieldDescriptorProto {
    * optional with `LABEL_OPTIONAL`.
    */
   proto3Optional: boolean;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export enum FieldDescriptorProto_Type {
@@ -229,7 +229,7 @@ export enum FieldDescriptorProto_Label {
 export interface OneofDescriptorProto {
   name: string;
   options: OneofOptions | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes an enum type. */
@@ -250,7 +250,7 @@ export interface EnumDescriptorProto {
    * be reserved once.
    */
   reservedName: string[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /**
@@ -266,7 +266,7 @@ export interface EnumDescriptorProto_EnumReservedRange {
   start: number;
   /** Inclusive. */
   end: number;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes a value within an enum. */
@@ -274,7 +274,7 @@ export interface EnumValueDescriptorProto {
   name: string;
   number: number;
   options: EnumValueOptions | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes a service. */
@@ -282,7 +282,7 @@ export interface ServiceDescriptorProto {
   name: string;
   method: MethodDescriptorProto[];
   options: ServiceOptions | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Describes a method of a service. */
@@ -301,7 +301,7 @@ export interface MethodDescriptorProto {
   clientStreaming: boolean;
   /** Identifies if server streams multiple server messages */
   serverStreaming: boolean;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface FileOptions {
@@ -423,7 +423,7 @@ export interface FileOptions {
    * See the documentation for the "Options" section above.
    */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /** Generated classes can be optimized for speed or code size. */
@@ -498,7 +498,7 @@ export interface MessageOptions {
   mapEntry: boolean;
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface FieldOptions {
@@ -572,7 +572,7 @@ export interface FieldOptions {
   weak: boolean;
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export enum FieldOptions_CType {
@@ -596,7 +596,7 @@ export enum FieldOptions_JSType {
 export interface OneofOptions {
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface EnumOptions {
@@ -614,7 +614,7 @@ export interface EnumOptions {
   deprecated: boolean;
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface EnumValueOptions {
@@ -627,7 +627,7 @@ export interface EnumValueOptions {
   deprecated: boolean;
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface ServiceOptions {
@@ -640,7 +640,7 @@ export interface ServiceOptions {
   deprecated: boolean;
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface MethodOptions {
@@ -654,7 +654,7 @@ export interface MethodOptions {
   idempotencyLevel: MethodOptions_IdempotencyLevel;
   /** The parser stores options it doesn't recognize here. See above. */
   uninterpretedOption: UninterpretedOption[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /**
@@ -691,7 +691,7 @@ export interface UninterpretedOption {
   doubleValue: number;
   stringValue: Uint8Array;
   aggregateValue: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /**
@@ -704,7 +704,7 @@ export interface UninterpretedOption {
 export interface UninterpretedOption_NamePart {
   namePart: string;
   isExtension: boolean;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /**
@@ -758,7 +758,7 @@ export interface SourceCodeInfo {
    *   be recorded in the future.
    */
   location: SourceCodeInfo_Location[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface SourceCodeInfo_Location {
@@ -848,7 +848,7 @@ export interface SourceCodeInfo_Location {
   leadingComments: string;
   trailingComments: string;
   leadingDetachedComments: string[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 /**
@@ -862,7 +862,7 @@ export interface GeneratedCodeInfo {
    * of its generating .proto file.
    */
   annotation: GeneratedCodeInfo_Annotation[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GeneratedCodeInfo_Annotation {
@@ -884,7 +884,7 @@ export interface GeneratedCodeInfo_Annotation {
    * the last relevant byte (so the length of the text = end - begin).
    */
   end: number;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseFileDescriptorSet(): FileDescriptorSet {
@@ -897,8 +897,7 @@ export const FileDescriptorSet = {
       FileDescriptorProto.encode(v!, writer.uint32(10).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1008,8 +1007,7 @@ export const FileDescriptorProto = {
       writer.uint32(98).string(message.syntax);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1204,8 +1202,7 @@ export const DescriptorProto = {
       writer.uint32(82).string(v!);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1333,8 +1330,7 @@ export const DescriptorProto_ExtensionRange = {
       ExtensionRangeOptions.encode(message.options, writer.uint32(26).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1410,8 +1406,7 @@ export const DescriptorProto_ReservedRange = {
       writer.uint32(16).int32(message.end);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1477,8 +1472,7 @@ export const ExtensionRangeOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1580,8 +1574,7 @@ export const FieldDescriptorProto = {
       writer.uint32(136).bool(message.proto3Optional);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1713,8 +1706,7 @@ export const OneofDescriptorProto = {
       OneofOptions.encode(message.options, writer.uint32(18).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1792,8 +1784,7 @@ export const EnumDescriptorProto = {
       writer.uint32(42).string(v!);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1883,8 +1874,7 @@ export const EnumDescriptorProto_EnumReservedRange = {
       writer.uint32(16).int32(message.end);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1956,8 +1946,7 @@ export const EnumValueDescriptorProto = {
       EnumValueOptions.encode(message.options, writer.uint32(26).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2036,8 +2025,7 @@ export const ServiceDescriptorProto = {
       ServiceOptions.encode(message.options, writer.uint32(26).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2133,8 +2121,7 @@ export const MethodDescriptorProto = {
       writer.uint32(48).bool(message.serverStreaming);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2311,8 +2298,7 @@ export const FileOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2530,8 +2516,7 @@ export const MessageOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2645,8 +2630,7 @@ export const FieldOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2747,8 +2731,7 @@ export const OneofOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2813,8 +2796,7 @@ export const EnumOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2890,8 +2872,7 @@ export const EnumValueOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -2960,8 +2941,7 @@ export const ServiceOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3033,8 +3013,7 @@ export const MethodOptions = {
       UninterpretedOption.encode(v!, writer.uint32(7994).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3134,8 +3113,7 @@ export const UninterpretedOption = {
       writer.uint32(66).string(message.aggregateValue);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3239,8 +3217,7 @@ export const UninterpretedOption_NamePart = {
       writer.uint32(16).bool(message.isExtension);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3306,8 +3283,7 @@ export const SourceCodeInfo = {
       SourceCodeInfo_Location.encode(v!, writer.uint32(10).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3389,8 +3365,7 @@ export const SourceCodeInfo_Location = {
       writer.uint32(50).string(v!);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3497,8 +3472,7 @@ export const GeneratedCodeInfo = {
       GeneratedCodeInfo_Annotation.encode(v!, writer.uint32(10).fork()).ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3568,8 +3542,7 @@ export const GeneratedCodeInfo_Annotation = {
       writer.uint32(32).int32(message.end);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -3649,10 +3622,10 @@ export const GeneratedCodeInfo_Annotation = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/unknown-fields/options.ts b/integration/unknown-fields/options.ts
index a11f4214..4f2081e9 100644
--- a/integration/unknown-fields/options.ts
+++ b/integration/unknown-fields/options.ts
@@ -14,15 +14,15 @@ export interface MyMessage {
   foo2?: number | undefined;
   bar?: string | undefined;
   quux?: string | undefined;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface RequestType {
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface ResponseType {
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseMyMessage(): MyMessage {
@@ -44,8 +44,7 @@ export const MyMessage = {
       writer.uint32(34).string(message.quux);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -122,8 +121,7 @@ function createBaseRequestType(): RequestType {
 export const RequestType = {
   encode(message: RequestType, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -172,8 +170,7 @@ function createBaseResponseType(): ResponseType {
 export const ResponseType = {
   encode(message: ResponseType, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
diff --git a/integration/unknown-fields/something/something.ts b/integration/unknown-fields/something/something.ts
index 8e083f96..48b9ebde 100644
--- a/integration/unknown-fields/something/something.ts
+++ b/integration/unknown-fields/something/something.ts
@@ -6,7 +6,7 @@ export const protobufPackage = "something";
 export interface Something {
   hello: string;
   foo: number[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseSomething(): Something {
@@ -24,8 +24,7 @@ export const Something = {
     }
     writer.ldelim();
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
diff --git a/integration/use-date-false/google/protobuf/timestamp.ts b/integration/use-date-false/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/use-date-false/google/protobuf/timestamp.ts
+++ b/integration/use-date-false/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-date-string/google/protobuf/timestamp.ts b/integration/use-date-string/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/use-date-string/google/protobuf/timestamp.ts
+++ b/integration/use-date-string/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-date-true/google/protobuf/timestamp.ts b/integration/use-date-true/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/use-date-true/google/protobuf/timestamp.ts
+++ b/integration/use-date-true/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-json-wire-format/google/protobuf/duration.ts b/integration/use-json-wire-format/google/protobuf/duration.ts
index 2ec402df..a5338b56 100644
--- a/integration/use-json-wire-format/google/protobuf/duration.ts
+++ b/integration/use-json-wire-format/google/protobuf/duration.ts
@@ -68,7 +68,9 @@ export interface Duration {
    * to +315,576,000,000 inclusive. Note: these bounds are computed from:
    * 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
    */
-  seconds?: number;
+  seconds?:
+    | number
+    | undefined;
   /**
    * Signed fractions of a second at nanosecond resolution of the span
    * of time. Durations less than one second are represented with a 0
@@ -77,5 +79,5 @@ export interface Duration {
    * of the same sign as the `seconds` field. Must be from -999,999,999
    * to +999,999,999 inclusive.
    */
-  nanos?: number;
+  nanos?: number | undefined;
 }
diff --git a/integration/use-json-wire-format/google/protobuf/field_mask.ts b/integration/use-json-wire-format/google/protobuf/field_mask.ts
index a97d9029..db1f33cb 100644
--- a/integration/use-json-wire-format/google/protobuf/field_mask.ts
+++ b/integration/use-json-wire-format/google/protobuf/field_mask.ts
@@ -204,5 +204,5 @@ export const protobufPackage = "google.protobuf";
  */
 export interface FieldMask {
   /** The set of field mask paths. */
-  paths?: string[];
+  paths?: string[] | undefined;
 }
diff --git a/integration/use-json-wire-format/google/protobuf/timestamp.ts b/integration/use-json-wire-format/google/protobuf/timestamp.ts
index 5f188796..cfd3d8c8 100644
--- a/integration/use-json-wire-format/google/protobuf/timestamp.ts
+++ b/integration/use-json-wire-format/google/protobuf/timestamp.ts
@@ -99,12 +99,14 @@ export interface Timestamp {
    * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
    * 9999-12-31T23:59:59Z inclusive.
    */
-  seconds?: number;
+  seconds?:
+    | number
+    | undefined;
   /**
    * Non-negative fractions of a second at nanosecond resolution. Negative
    * second values with fractions must still have non-negative nanos values
    * that count forward in time. Must be from 0 to 999,999,999
    * inclusive.
    */
-  nanos?: number;
+  nanos?: number | undefined;
 }
diff --git a/integration/use-json-wire-format/use-json-wire-format.ts b/integration/use-json-wire-format/use-json-wire-format.ts
index 8f9d401e..15439502 100644
--- a/integration/use-json-wire-format/use-json-wire-format.ts
+++ b/integration/use-json-wire-format/use-json-wire-format.ts
@@ -3,8 +3,8 @@
 export const protobufPackage = "";
 
 export interface Todo {
-  id?: string;
-  timestamp?: string;
-  duration?: string;
-  updateMask?: string;
+  id?: string | undefined;
+  timestamp?: string | undefined;
+  duration?: string | undefined;
+  updateMask?: string | undefined;
 }
diff --git a/integration/use-map-type/google/protobuf/timestamp.ts b/integration/use-map-type/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/use-map-type/google/protobuf/timestamp.ts
+++ b/integration/use-map-type/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-map-type/use-map-type.ts b/integration/use-map-type/use-map-type.ts
index 0c494fe2..65f8fccc 100644
--- a/integration/use-map-type/use-map-type.ts
+++ b/integration/use-map-type/use-map-type.ts
@@ -687,10 +687,10 @@ export const Maps_MapOfTimestampsEntry = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-optionals-all/google/protobuf/timestamp.ts b/integration/use-optionals-all/google/protobuf/timestamp.ts
index 24a0bab6..dfd52f72 100644
--- a/integration/use-optionals-all/google/protobuf/timestamp.ts
+++ b/integration/use-optionals-all/google/protobuf/timestamp.ts
@@ -101,14 +101,16 @@ export interface Timestamp {
    * 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
    * 9999-12-31T23:59:59Z inclusive.
    */
-  seconds?: number;
+  seconds?:
+    | number
+    | undefined;
   /**
    * Non-negative fractions of a second at nanosecond resolution. Negative
    * second values with fractions must still have non-negative nanos values
    * that count forward in time. Must be from 0 to 999,999,999
    * inclusive.
    */
-  nanos?: number;
+  nanos?: number | undefined;
 }
 
 function createBaseTimestamp(): Timestamp {
@@ -182,10 +184,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-readonly-types/google/protobuf/struct.ts b/integration/use-readonly-types/google/protobuf/struct.ts
index 7b43ad34..10fbb618 100644
--- a/integration/use-readonly-types/google/protobuf/struct.ts
+++ b/integration/use-readonly-types/google/protobuf/struct.ts
@@ -72,7 +72,8 @@ export interface Value {
     | { readonly $case: "stringValue"; readonly stringValue: string }
     | { readonly $case: "boolValue"; readonly boolValue: boolean }
     | { readonly $case: "structValue"; readonly structValue: { readonly [key: string]: any } | undefined }
-    | { readonly $case: "listValue"; readonly listValue: ReadonlyArray<any> | undefined };
+    | { readonly $case: "listValue"; readonly listValue: ReadonlyArray<any> | undefined }
+    | undefined;
 }
 
 /**
diff --git a/integration/use-readonly-types/use-readonly-types.ts b/integration/use-readonly-types/use-readonly-types.ts
index a5f185be..e901e875 100644
--- a/integration/use-readonly-types/use-readonly-types.ts
+++ b/integration/use-readonly-types/use-readonly-types.ts
@@ -19,7 +19,7 @@ export interface Entity {
   readonly oneOfValue?: { readonly $case: "theStringValue"; readonly theStringValue: string } | {
     readonly $case: "theIntValue";
     readonly theIntValue: number;
-  };
+  } | undefined;
 }
 
 export interface SubEntity {
diff --git a/integration/value/google/protobuf/wrappers.ts b/integration/value/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/value/google/protobuf/wrappers.ts
+++ b/integration/value/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/vector-tile/vector_tile.ts b/integration/vector-tile/vector_tile.ts
index 93d2bd8d..ba885681 100644
--- a/integration/vector-tile/vector_tile.ts
+++ b/integration/vector-tile/vector_tile.ts
@@ -539,10 +539,10 @@ export const Tile_Layer = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/wrappers-regression/google/protobuf/timestamp.ts b/integration/wrappers-regression/google/protobuf/timestamp.ts
index e4b5e083..dc3946fe 100644
--- a/integration/wrappers-regression/google/protobuf/timestamp.ts
+++ b/integration/wrappers-regression/google/protobuf/timestamp.ts
@@ -182,10 +182,10 @@ export const Timestamp = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/wrappers-regression/google/protobuf/wrappers.ts b/integration/wrappers-regression/google/protobuf/wrappers.ts
index 99125575..356be214 100644
--- a/integration/wrappers-regression/google/protobuf/wrappers.ts
+++ b/integration/wrappers-regression/google/protobuf/wrappers.ts
@@ -599,10 +599,10 @@ export const BytesValue = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/jest.config.js b/jest.config.js
index 4de15779..ecce6b20 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -1,7 +1,16 @@
 /**  @type {import('@jest/types').Config.ProjectConfig} */
 module.exports = {
   transform: {
-    '^.+\\.ts$': 'ts-jest',
+    '^.+\\.ts$': [
+      'ts-jest',
+      {
+        // Note: We are limited to one single TS config even though we have
+        // vastly different TS configs for the sources and the tests. So we'll
+        // make do with a **very** loose config file. Any type checking should
+        // be done with `yarn tsc:check`.
+        tsconfig: 'tsconfig.json',
+      }
+    ],
   },
   resolver: 'jest-ts-webcompat-resolver',
   moduleFileExtensions: ['js', 'ts'],
diff --git a/package.json b/package.json
index b329ec7c..c126e866 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "proto2pbjs": "docker compose run --rm protoc pbjs.sh",
     "bin2ts": "docker compose run --rm protoc codegen.sh",
     "test": "yarn jest -c jest.config.js --maxWorkers=2",
+    "tsc:check": "./tsc-check.sh tsconfig.json integration/tsconfig.json integration/tsconfig.proto.json protos/tsconfig.json",
     "format": "prettier --write {src,tests}/**/*.ts integration/*.ts",
     "format:check": "prettier --list-different {src,tests}/**/*.ts",
     "setup:docker": "docker compose build",
@@ -42,8 +43,9 @@
     "@semantic-release/github": "^9.0.2",
     "@semantic-release/npm": "^10.0.3",
     "@semantic-release/release-notes-generator": "^11.0.2",
+    "@tsconfig/strictest": "^2.0.1",
     "@types/jest": "^29.5.2",
-    "@types/node": "^14.14.37",
+    "@types/node": "^16.18.38",
     "chokidar": "^3.5.3",
     "jest": "^29.5.0",
     "jest-ts-webcompat-resolver": "^1.0.0",
diff --git a/src/generate-grpc-web.ts b/src/generate-grpc-web.ts
index 57f8ba48..9b96fe40 100644
--- a/src/generate-grpc-web.ts
+++ b/src/generate-grpc-web.ts
@@ -258,17 +258,16 @@ function createPromiseUnaryMethod(ctx: Context): Code {
       ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
     ): Promise<any> {
       const request = { ..._request, ...methodDesc.requestType };
-      const maybeCombinedMetadata =
-        metadata && this.options.metadata
-          ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-          : metadata || this.options.metadata;
+      const maybeCombinedMetadata = metadata && this.options.metadata
+        ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
+        : metadata ?? this.options.metadata;
       return new Promise((resolve, reject) => {
         ${useAbortSignal ? `const client =` : ""} ${grpc}.unary(methodDesc, {
           request,
           host: this.host,
-          metadata: maybeCombinedMetadata,
-          transport: this.options.transport,
-          debug: this.options.debug,
+          metadata: maybeCombinedMetadata ?? {},
+          ...(this.options.transport !== undefined ? {transport: this.options.transport} : {}),
+          debug: this.options.debug ?? false,
           onEnd: function (response) {
             if (response.status === grpc.Code.OK) {
               resolve(response.message!.toObject());
@@ -304,17 +303,16 @@ function createObservableUnaryMethod(ctx: Context): Code {
       ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
     ): ${observableType(ctx)}<any> {
       const request = { ..._request, ...methodDesc.requestType };
-      const maybeCombinedMetadata =
-        metadata && this.options.metadata
-          ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-          : metadata || this.options.metadata;
+      const maybeCombinedMetadata = metadata && this.options.metadata
+        ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
+        : metadata ?? this.options.metadata;
       return new Observable(observer => {
         ${useAbortSignal ? `const client =` : ""} ${grpc}.unary(methodDesc, {
           request,
           host: this.host,
-          metadata: maybeCombinedMetadata,
-          transport: this.options.transport,
-          debug: this.options.debug,
+          metadata: maybeCombinedMetadata ?? {},
+          ...(this.options.transport !== undefined ? {transport: this.options.transport} : {}),
+          debug: this.options.debug ?? false,
           onEnd: (next) => {
             if (next.status !== 0) {
               const err = new ${ctx.utils.GrpcWebError}(next.statusMessage, next.status, next.trailers);
@@ -353,21 +351,21 @@ function createInvokeMethod(ctx: Context) {
       metadata: grpc.Metadata | undefined,
       ${useAbortSignal ? "abortSignal?: AbortSignal," : ""}
     ): ${observableType(ctx)}<any> {
-      const upStreamCodes = this.options.upStreamRetryCodes || [];
+      const upStreamCodes = this.options.upStreamRetryCodes ?? [];
       const DEFAULT_TIMEOUT_TIME: number = 3_000;
       const request = { ..._request, ...methodDesc.requestType };
-      const maybeCombinedMetadata =
-      metadata && this.options.metadata
+      const transport = this.options.streamingTransport ?? this.options.transport;
+      const maybeCombinedMetadata = metadata && this.options.metadata
         ? new ${BrowserHeaders}({ ...this.options?.metadata.headersMap, ...metadata?.headersMap })
-        : metadata || this.options.metadata;
+        : metadata ?? this.options.metadata;
       return new Observable(observer => {
         const upStream = (() => {
           const client = ${grpc}.invoke(methodDesc, {
             host: this.host,
             request,
-            transport: this.options.streamingTransport || this.options.transport,
-            metadata: maybeCombinedMetadata,
-            debug: this.options.debug,
+            ...(transport !== undefined ? {transport} : {}),
+            metadata: maybeCombinedMetadata ?? {},
+            debug: this.options.debug ?? false,
             onMessage: (next) => observer.next(next),
             onEnd: (code: ${grpc}.Code, message: string, trailers: ${grpc}.Metadata) => {
               if (code === 0) {
diff --git a/src/generate-services.ts b/src/generate-services.ts
index 3deb8bb7..2fe599fc 100644
--- a/src/generate-services.ts
+++ b/src/generate-services.ts
@@ -259,7 +259,7 @@ function generateBatchingRpcMethod(ctx: Context, batchMethod: BatchMethod): Code
     // If the return type is a map, lookup each key in the result
     lambda.push(code`
       return this.${methodDesc.formattedName}(ctx, request).then(res => {
-        return ${inputFieldName}.map(key => res.${outputFieldName}[key])
+        return ${inputFieldName}.map(key => res.${outputFieldName}[key] ?? ${ctx.utils.fail}())
       });
     `);
   } else {
diff --git a/src/main.ts b/src/main.ts
index d46340f6..a4860b12 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -401,7 +401,8 @@ export type Utils = ReturnType<typeof makeDeepPartial> &
   ReturnType<typeof makeComparisonUtils> &
   ReturnType<typeof makeNiceGrpcServerStreamingMethodResult> &
   ReturnType<typeof makeGrpcWebErrorClass> &
-  ReturnType<typeof makeExtensionClass>;
+  ReturnType<typeof makeExtensionClass> &
+  ReturnType<typeof makeAssertionUtils>;
 
 /** These are runtime utility methods used by the generated code. */
 export function makeUtils(options: Options): Utils {
@@ -417,6 +418,7 @@ export function makeUtils(options: Options): Utils {
     ...makeNiceGrpcServerStreamingMethodResult(),
     ...makeGrpcWebErrorClass(bytes),
     ...makeExtensionClass(options),
+    ...makeAssertionUtils(),
   };
 }
 
@@ -531,10 +533,10 @@ function makeByteUtils() {
   const globalThis = conditionalOutput(
     "tsProtoGlobalThis",
     code`
-      declare var self: any | undefined;
-      declare var window: any | undefined;
-      declare var global: any | undefined;
-      var tsProtoGlobalThis: any = (() => {
+      declare const self: any | undefined;
+      declare const window: any | undefined;
+      declare const global: any | undefined;
+      const tsProtoGlobalThis: any = (() => {
         if (typeof globalThis !== "undefined") return globalThis;
         if (typeof self !== "undefined") return self;
         if (typeof window !== "undefined") return window;
@@ -834,6 +836,19 @@ function makeExtensionClass(options: Options) {
   return { Extension };
 }
 
+function makeAssertionUtils() {
+  const fail = conditionalOutput(
+    "fail",
+    code`
+      function fail(message?: string): never {
+        throw new Error(message ?? "Failed");
+      }
+    `
+  );
+
+  return { fail };
+}
+
 // Create the interface with properties
 function generateInterfaceDeclaration(
   ctx: Context,
@@ -870,13 +885,16 @@ function generateInterfaceDeclaration(
     maybeAddComment(info, chunks, fieldDesc.options?.deprecated);
 
     const name = maybeSnakeToCamel(fieldDesc.name, options);
-    const type = toTypeName(ctx, messageDesc, fieldDesc);
-    const q = isOptionalProperty(fieldDesc, messageDesc.options, options) ? "?" : "";
-    chunks.push(code`${maybeReadonly(options)}${name}${q}: ${type}, `);
+    const isOptional = isOptionalProperty(fieldDesc, messageDesc.options, options);
+    const type = toTypeName(ctx, messageDesc, fieldDesc, isOptional);
+    if (isOptional && !type.toString().includes("undefined")) {
+      console.warn(name, type);
+    }
+    chunks.push(code`${maybeReadonly(options)}${name}${isOptional ? "?" : ""}: ${type}, `);
   });
 
   if (ctx.options.unknownFields) {
-    chunks.push(code`_unknownFields?: {[key: number]: Uint8Array[]},`);
+    chunks.push(code`_unknownFields?: {[key: number]: Uint8Array[]} | undefined,`);
   }
 
   chunks.push(code`}`);
@@ -902,7 +920,7 @@ function generateOneofProperty(
   );
 
   const name = maybeSnakeToCamel(messageDesc.oneofDecl[oneofIndex].name, options);
-  return code`${mbReadonly}${name}?: ${unionType},`;
+  return code`${mbReadonly}${name}?: ${unionType} | undefined,`;
 
   /*
   // Ideally we'd put the comments for each oneof field next to the anonymous
@@ -1428,8 +1446,7 @@ function generateEncode(ctx: Context, fullName: string, messageDesc: DescriptorP
 
   if (options.unknownFields) {
     chunks.push(code`if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -1639,7 +1656,7 @@ function generateExtension(ctx: Context, message: DescriptorProto | undefined, e
 
       // start loop over all buffers
       chunks.push(code`
-        for (var buffer of input) {
+        for (const buffer of input) {
           const reader = ${Reader}.create(buffer);
       `);
 
@@ -1669,7 +1686,7 @@ function generateExtension(ctx: Context, message: DescriptorProto | undefined, e
     } else {
       // pick the last entry, since it overrides all previous entries if not repeated
       chunks.push(code`
-          const reader = ${Reader}.create(input[input.length -1]);
+          const reader = ${Reader}.create(input[input.length -1] ?? ${ctx.utils.fail}());
           return ${readSnippet};
         },
       `);
diff --git a/src/types.ts b/src/types.ts
index bbee5701..1a074c06 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -561,16 +561,9 @@ export function messageToTypeName(
   // them to basic built-in types, we union the type with undefined to
   // indicate the value is optional. Exceptions:
   // - If the field is repeated, values cannot be undefined.
-  // - If useOptionals='messages' or useOptionals='all', all non-scalar types
-  //   are already optional properties, so there's no need for that union.
   let valueType = valueTypeName(ctx, protoType);
   if (!typeOptions.keepValueType && valueType) {
-    if (
-      !!typeOptions.repeated ||
-      options.useOptionals === true ||
-      options.useOptionals === "messages" ||
-      options.useOptionals === "all"
-    ) {
+    if (typeOptions.repeated ?? false) {
       return valueType;
     }
     return code`${valueType} | undefined`;
@@ -605,27 +598,39 @@ export function getEnumMethod(ctx: Context, enumProtoType: string, methodSuffix:
 }
 
 /** Return the TypeName for any field (primitive/message/etc.) as exposed in the interface. */
-export function toTypeName(ctx: Context, messageDesc: DescriptorProto | undefined, field: FieldDescriptorProto): Code {
+export function toTypeName(
+  ctx: Context,
+  messageDesc: DescriptorProto | undefined,
+  field: FieldDescriptorProto,
+  ensureOptional = false
+): Code {
+  function finalize(type: Code, isOptional: boolean) {
+    if (isOptional) {
+      return code`${type} | undefined`;
+    }
+    return type;
+  }
+
   let type = basicTypeName(ctx, field, { keepValueType: false });
   if (isRepeated(field)) {
     const mapType = messageDesc ? detectMapType(ctx, messageDesc, field) : false;
     if (mapType) {
       const { keyType, valueType } = mapType;
       if (ctx.options.useMapType) {
-        return code`Map<${keyType}, ${valueType}>`;
+        return finalize(code`Map<${keyType}, ${valueType}>`, ensureOptional);
       }
-      return code`{ [key: ${keyType} ]: ${valueType} }`;
+      return finalize(code`{ [key: ${keyType} ]: ${valueType} }`, ensureOptional);
     }
     if (ctx.options.useReadonlyTypes) {
-      return code`readonly ${type}[]`;
+      return finalize(code`readonly ${type}[]`, ensureOptional);
     }
-    return code`${type}[]`;
+    return finalize(code`${type}[]`, ensureOptional);
   }
 
   if (isValueType(ctx, field)) {
     // google.protobuf.*Value types are already unioned with `undefined`
     // in messageToTypeName, so no need to consider them for that here.
-    return type;
+    return finalize(type, false);
   }
 
   // By default (useOptionals='none', oneof=properties), non-scalar fields
@@ -640,17 +645,15 @@ export function toTypeName(ctx: Context, messageDesc: DescriptorProto | undefine
   // clause, spelling each option out inside a large type union. No need for
   // union with `undefined` here, either.
   const { options } = ctx;
-  if (
+  return finalize(
+    type,
     (!isWithinOneOf(field) &&
       isMessage(field) &&
       (options.useOptionals === false || options.useOptionals === "none")) ||
-    (isWithinOneOf(field) && options.oneof === OneofOption.PROPERTIES) ||
-    (isWithinOneOf(field) && field.proto3Optional)
-  ) {
-    return code`${type} | undefined`;
-  }
-
-  return type;
+      (isWithinOneOf(field) && options.oneof === OneofOption.PROPERTIES) ||
+      (isWithinOneOf(field) && field.proto3Optional) ||
+      ensureOptional
+  );
 }
 
 export function detectMapType(
diff --git a/tsc-check.sh b/tsc-check.sh
new file mode 100755
index 00000000..b6d4ad12
--- /dev/null
+++ b/tsc-check.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+for path in "$@"; do
+  echo Checking ${path}
+  yarn tsc --noEmit -p ${path}
+done
diff --git a/tsconfig.json b/tsconfig.json
index c74fef3c..bdd454a1 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -8,5 +8,5 @@
     "skipLibCheck": true,
     "experimentalDecorators": true
   },
-  "include": ["src"]
+  "include": ["src", "integration/codegen.ts", "tests"],
 }
diff --git a/yarn.lock b/yarn.lock
index 78014992..bcea3621 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1868,6 +1868,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@tsconfig/strictest@npm:^2.0.1":
+  version: 2.0.1
+  resolution: "@tsconfig/strictest@npm:2.0.1"
+  checksum: 3a9b913e06c20fee0a002fc22a925370525dd917853dee2e0082de763b10a61a69d72d60d02e243629da94b3e0b98843469a3b063c0ab2a5d6716183867dd114
+  languageName: node
+  linkType: hard
+
 "@tufjs/canonical-json@npm:1.0.0":
   version: 1.0.0
   resolution: "@tufjs/canonical-json@npm:1.0.0"
@@ -1991,7 +1998,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@types/node@npm:*, @types/node@npm:>=12.12.47, @types/node@npm:^14.14.37":
+"@types/node@npm:*, @types/node@npm:>=12.12.47":
   version: 14.14.37
   resolution: "@types/node@npm:14.14.37"
   checksum: 647e671ac3815f428a07ae9353ebab93c5335d4d0a461ca837a79eebf55c04f28bee80f0c43881cdd8696f595feb112bc8eb74f6124125e02f79a7677374c187
@@ -2012,6 +2019,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@types/node@npm:^16.18.38":
+  version: 16.18.38
+  resolution: "@types/node@npm:16.18.38"
+  checksum: a3baa141e49ce94486f083eea1240cf38479a73ba663e1bf3f52f85b466125821b6e3ea85ded38fde3901530aca4601291395a50eefcea533a4f3b45171bda28
+  languageName: node
+  linkType: hard
+
 "@types/normalize-package-data@npm:^2.4.0":
   version: 2.4.0
   resolution: "@types/normalize-package-data@npm:2.4.0"
@@ -7992,8 +8006,9 @@ __metadata:
     "@semantic-release/github": ^9.0.2
     "@semantic-release/npm": ^10.0.3
     "@semantic-release/release-notes-generator": ^11.0.2
+    "@tsconfig/strictest": ^2.0.1
     "@types/jest": ^29.5.2
-    "@types/node": ^14.14.37
+    "@types/node": ^16.18.38
     "@types/object-hash": ^3.0.2
     case-anything: ^2.1.10
     chokidar: ^3.5.3


Test patch:
diff --git a/integration/extensions/test.ts b/integration/extensions/test.ts
index c840b079..03cf234d 100644
--- a/integration/extensions/test.ts
+++ b/integration/extensions/test.ts
@@ -44,19 +44,19 @@ export function enumToJSON(object: Enum): string {
 }
 
 export interface Extendable {
-  field?: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  field?: string | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface Nested {
-  field?: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  field?: string | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface Group {
-  name?: string;
-  value?: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  name?: string | undefined;
+  value?: string | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseExtendable(): Extendable {
@@ -69,8 +69,7 @@ export const Extendable = {
       writer.uint32(10).string(message.field);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -216,7 +215,7 @@ export const Nested = {
     },
     decode: (tag: number, input: Uint8Array[]): Nested[] => {
       const values: Nested[] = [];
-      for (var buffer of input) {
+      for (const buffer of input) {
         const reader = _m0.Reader.create(buffer);
         values.push(Nested.decode(reader, reader.uint32()));
       }
@@ -230,8 +229,7 @@ export const Nested = {
       writer.uint32(10).string(message.field);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -317,8 +315,7 @@ export const Group = {
       writer.uint32(18).string(message.value);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -422,7 +419,7 @@ export const packed: Extension<number[]> = {
   },
   decode: (tag: number, input: Uint8Array[]): number[] => {
     const values: number[] = [];
-    for (var buffer of input) {
+    for (const buffer of input) {
       const reader = _m0.Reader.create(buffer);
       if (tag == 42) {
         const end2 = reader.uint32() + reader.pos;
@@ -457,7 +454,7 @@ export const repeated: Extension<number[]> = {
   },
   decode: (tag: number, input: Uint8Array[]): number[] => {
     const values: number[] = [];
-    for (var buffer of input) {
+    for (const buffer of input) {
       const reader = _m0.Reader.create(buffer);
       if (tag == 50) {
         const end2 = reader.uint32() + reader.pos;
@@ -488,7 +485,7 @@ export const bytes: Extension<Uint8Array> = {
     return encoded;
   },
   decode: (tag: number, input: Uint8Array[]): Uint8Array => {
-    const reader = _m0.Reader.create(input[input.length - 1]);
+    const reader = _m0.Reader.create(input[input.length - 1] ?? fail());
     return reader.bytes();
   },
 };
@@ -508,7 +505,7 @@ export const string: Extension<string> = {
     return encoded;
   },
   decode: (tag: number, input: Uint8Array[]): string => {
-    const reader = _m0.Reader.create(input[input.length - 1]);
+    const reader = _m0.Reader.create(input[input.length - 1] ?? fail());
     return reader.string();
   },
 };
@@ -528,7 +525,7 @@ export const long: Extension<Long> = {
     return encoded;
   },
   decode: (tag: number, input: Uint8Array[]): Long => {
-    const reader = _m0.Reader.create(input[input.length - 1]);
+    const reader = _m0.Reader.create(input[input.length - 1] ?? fail());
     return reader.int64() as Long;
   },
 };
@@ -548,7 +545,7 @@ export const fixed: Extension<Long> = {
     return encoded;
   },
   decode: (tag: number, input: Uint8Array[]): Long => {
-    const reader = _m0.Reader.create(input[input.length - 1]);
+    const reader = _m0.Reader.create(input[input.length - 1] ?? fail());
     return reader.fixed64() as Long;
   },
 };
@@ -568,7 +565,7 @@ export const enumField: Extension<Enum> = {
     return encoded;
   },
   decode: (tag: number, input: Uint8Array[]): Enum => {
-    const reader = _m0.Reader.create(input[input.length - 1]);
+    const reader = _m0.Reader.create(input[input.length - 1] ?? fail());
     return reader.int32() as any;
   },
 };
@@ -586,7 +583,7 @@ export const group: Extension<Group> = {
     return encoded;
   },
   decode: (tag: number, input: Uint8Array[]): Group => {
-    const reader = _m0.Reader.create(input[input.length - 1]);
+    const reader = _m0.Reader.create(input[input.length - 1] ?? fail());
     return Group.decode(reader);
   },
 };
@@ -623,3 +620,7 @@ export interface Extension<T> {
   repeated: boolean;
   packed: boolean;
 }
+
+function fail(message?: string): never {
+  throw new Error(message ?? "Failed");
+}
diff --git a/integration/from-partial-no-initialize/test.ts b/integration/from-partial-no-initialize/test.ts
index 9fd609d8..6407b23d 100644
--- a/integration/from-partial-no-initialize/test.ts
+++ b/integration/from-partial-no-initialize/test.ts
@@ -4,17 +4,17 @@ import * as _m0 from "protobufjs/minimal";
 export const protobufPackage = "";
 
 export interface TPartialMessage {
-  field?: string;
+  field?: string | undefined;
 }
 
 export interface TPartial {
-  number?: number;
-  string?: string;
-  map?: { [key: string]: string };
-  message?: TPartialMessage;
-  repeatedMessage?: TPartialMessage[];
-  repeatedString?: string[];
-  repeatedNumber?: number[];
+  number?: number | undefined;
+  string?: string | undefined;
+  map?: { [key: string]: string } | undefined;
+  message?: TPartialMessage | undefined;
+  repeatedMessage?: TPartialMessage[] | undefined;
+  repeatedString?: string[] | undefined;
+  repeatedNumber?: number[] | undefined;
 }
 
 export interface TPartial_MapEntry {
diff --git a/integration/groups/test.ts b/integration/groups/test.ts
index 1950a5ec..118998c2 100644
--- a/integration/groups/test.ts
+++ b/integration/groups/test.ts
@@ -4,51 +4,51 @@ import * as _m0 from "protobufjs/minimal";
 export const protobufPackage = "";
 
 export interface GroupsOptionalTest {
-  int1?: number;
-  group?: GroupsOptionalTest_Group;
-  int3?: number;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  int1?: number | undefined;
+  group?: GroupsOptionalTest_Group | undefined;
+  int3?: number | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsOptionalTest_Group {
-  key?: string;
-  value?: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  key?: string | undefined;
+  value?: string | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsRepeatedTest {
-  int1?: number[];
-  group?: GroupsRepeatedTest_Group[];
-  int3?: number[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  int1?: number[] | undefined;
+  group?: GroupsRepeatedTest_Group[] | undefined;
+  int3?: number[] | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsRepeatedTest_Group {
-  key?: string[];
-  value?: string[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  key?: string[] | undefined;
+  value?: string[] | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsNestedTest {
-  int1?: number[];
-  group?: GroupsNestedTest_Group[];
-  int3?: number[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  int1?: number[] | undefined;
+  group?: GroupsNestedTest_Group[] | undefined;
+  int3?: number[] | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsNestedTest_Group {
-  nested?: GroupsNestedTest_Group_Nested[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  nested?: GroupsNestedTest_Group_Nested[] | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsNestedTest_Group_Nested {
-  nested2?: GroupsNestedTest_Group_Nested_Nested2[];
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  nested2?: GroupsNestedTest_Group_Nested_Nested2[] | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface GroupsNestedTest_Group_Nested_Nested2 {
-  string1?: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  string1?: string | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseGroupsOptionalTest(): GroupsOptionalTest {
@@ -67,8 +67,7 @@ export const GroupsOptionalTest = {
       writer.uint32(24).int32(message.int3);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -179,8 +178,7 @@ export const GroupsOptionalTest_Group = {
       writer.uint32(18).string(message.value);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -291,8 +289,7 @@ export const GroupsRepeatedTest = {
       writer.ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -453,8 +450,7 @@ export const GroupsRepeatedTest_Group = {
       }
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -579,8 +575,7 @@ export const GroupsNestedTest = {
       writer.ldelim();
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -736,8 +731,7 @@ export const GroupsNestedTest_Group = {
       }
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -833,8 +827,7 @@ export const GroupsNestedTest_Group_Nested = {
       }
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -930,8 +923,7 @@ export const GroupsNestedTest_Group_Nested_Nested2 = {
       writer.uint32(10).string(message.string1);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
diff --git a/integration/map-bigint-optional/test.ts b/integration/map-bigint-optional/test.ts
index 5da264d4..6c91a8b5 100644
--- a/integration/map-bigint-optional/test.ts
+++ b/integration/map-bigint-optional/test.ts
@@ -5,14 +5,14 @@ import * as _m0 from "protobufjs/minimal";
 export const protobufPackage = "";
 
 export interface MapBigInt {
-  map?: Map<bigint, bigint>;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  map?: Map<bigint, bigint> | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface MapBigInt_MapEntry {
   key: bigint;
   value: bigint;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseMapBigInt(): MapBigInt {
@@ -25,8 +25,7 @@ export const MapBigInt = {
       MapBigInt_MapEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim();
     });
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -138,8 +137,7 @@ export const MapBigInt_MapEntry = {
       writer.uint32(16).int64(message.value.toString());
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
diff --git a/integration/map-long-optional/test.ts b/integration/map-long-optional/test.ts
index a7c0242b..436cfd39 100644
--- a/integration/map-long-optional/test.ts
+++ b/integration/map-long-optional/test.ts
@@ -5,14 +5,14 @@ import * as _m0 from "protobufjs/minimal";
 export const protobufPackage = "";
 
 export interface MapBigInt {
-  map?: Map<Long, Long>;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  map?: Map<Long, Long> | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface MapBigInt_MapEntry {
   key: Long;
   value: Long;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseMapBigInt(): MapBigInt {
@@ -25,8 +25,7 @@ export const MapBigInt = {
       MapBigInt_MapEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim();
     });
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -138,8 +137,7 @@ export const MapBigInt_MapEntry = {
       writer.uint32(16).int64(message.value);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -224,10 +222,10 @@ export const MapBigInt_MapEntry = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/map-longstring-optional/test.ts b/integration/map-longstring-optional/test.ts
index e4b0da81..1dfad9e5 100644
--- a/integration/map-longstring-optional/test.ts
+++ b/integration/map-longstring-optional/test.ts
@@ -5,14 +5,14 @@ import * as _m0 from "protobufjs/minimal";
 export const protobufPackage = "";
 
 export interface MapBigInt {
-  map?: Map<string, string>;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  map?: Map<string, string> | undefined;
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 export interface MapBigInt_MapEntry {
   key: string;
   value: string;
-  _unknownFields?: { [key: number]: Uint8Array[] };
+  _unknownFields?: { [key: number]: Uint8Array[] } | undefined;
 }
 
 function createBaseMapBigInt(): MapBigInt {
@@ -25,8 +25,7 @@ export const MapBigInt = {
       MapBigInt_MapEntry.encode({ key: key as any, value }, writer.uint32(10).fork()).ldelim();
     });
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
@@ -138,8 +137,7 @@ export const MapBigInt_MapEntry = {
       writer.uint32(16).int64(message.value);
     }
     if (message._unknownFields !== undefined) {
-      for (const key in message._unknownFields) {
-        const values = message._unknownFields[key];
+      for (const [key, values] of Object.entries(message._unknownFields)) {
         const tag = parseInt(key, 10);
         for (const value of values) {
           writer.uint32(tag);
diff --git a/integration/use-optionals-all/test.ts b/integration/use-optionals-all/test.ts
index 002e6529..24afe279 100644
--- a/integration/use-optionals-all/test.ts
+++ b/integration/use-optionals-all/test.ts
@@ -45,20 +45,20 @@ export function stateEnumToJSON(object: StateEnum): string {
 }
 
 export interface OptionalsTest {
-  id?: number;
-  child?: Child;
-  state?: StateEnum;
-  long?: number;
-  truth?: boolean;
-  description?: string;
-  data?: Uint8Array;
-  repId?: number[];
-  repChild?: Child[];
-  repState?: StateEnum[];
-  repLong?: number[];
-  repTruth?: boolean[];
-  repDescription?: string[];
-  repData?: Uint8Array[];
+  id?: number | undefined;
+  child?: Child | undefined;
+  state?: StateEnum | undefined;
+  long?: number | undefined;
+  truth?: boolean | undefined;
+  description?: string | undefined;
+  data?: Uint8Array | undefined;
+  repId?: number[] | undefined;
+  repChild?: Child[] | undefined;
+  repState?: StateEnum[] | undefined;
+  repLong?: number[] | undefined;
+  repTruth?: boolean[] | undefined;
+  repDescription?: string[] | undefined;
+  repData?: Uint8Array[] | undefined;
   optId?: number | undefined;
   optChild?: Child | undefined;
   optState?: StateEnum | undefined;
@@ -66,8 +66,8 @@ export interface OptionalsTest {
   optTruth?: boolean | undefined;
   optDescription?: string | undefined;
   optData?: Uint8Array | undefined;
-  translations?: { [key: string]: string };
-  timestamp?: Date;
+  translations?: { [key: string]: string } | undefined;
+  timestamp?: Date | undefined;
 }
 
 export interface OptionalsTest_TranslationsEntry {
@@ -676,10 +676,10 @@ export const Child = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/integration/use-optionals-no-undefined/test.ts b/integration/use-optionals-no-undefined/test.ts
index 78a69902..7125bf0a 100644
--- a/integration/use-optionals-no-undefined/test.ts
+++ b/integration/use-optionals-no-undefined/test.ts
@@ -44,20 +44,20 @@ export function stateEnumToJSON(object: StateEnum): string {
 }
 
 export interface OptionalsTest {
-  id?: number;
-  child?: Child;
-  state?: StateEnum;
-  long?: number;
-  truth?: boolean;
-  description?: string;
-  data?: Uint8Array;
-  repId?: number[];
-  repChild?: Child[];
-  repState?: StateEnum[];
-  repLong?: number[];
-  repTruth?: boolean[];
-  repDescription?: string[];
-  repData?: Uint8Array[];
+  id?: number | undefined;
+  child?: Child | undefined;
+  state?: StateEnum | undefined;
+  long?: number | undefined;
+  truth?: boolean | undefined;
+  description?: string | undefined;
+  data?: Uint8Array | undefined;
+  repId?: number[] | undefined;
+  repChild?: Child[] | undefined;
+  repState?: StateEnum[] | undefined;
+  repLong?: number[] | undefined;
+  repTruth?: boolean[] | undefined;
+  repDescription?: string[] | undefined;
+  repData?: Uint8Array[] | undefined;
   optId?: number | undefined;
   optChild?: Child | undefined;
   optState?: StateEnum | undefined;
@@ -65,7 +65,7 @@ export interface OptionalsTest {
   optTruth?: boolean | undefined;
   optDescription?: string | undefined;
   optData?: Uint8Array | undefined;
-  translations?: { [key: string]: string };
+  translations?: { [key: string]: string } | undefined;
 }
 
 export interface OptionalsTest_TranslationsEntry {
@@ -674,10 +674,10 @@ export const Child = {
   },
 };
 
-declare var self: any | undefined;
-declare var window: any | undefined;
-declare var global: any | undefined;
-var tsProtoGlobalThis: any = (() => {
+declare const self: any | undefined;
+declare const window: any | undefined;
+declare const global: any | undefined;
+const tsProtoGlobalThis: any = (() => {
   if (typeof globalThis !== "undefined") {
     return globalThis;
   }
diff --git a/tests/types-test.ts b/tests/types-test.ts
index 9d37b218..cea14e41 100644
--- a/tests/types-test.ts
+++ b/tests/types-test.ts
@@ -45,14 +45,14 @@ describe("types", () => {
         typeMap: new Map(),
         protoType: ".google.protobuf.StringValue",
         options: { ...defaultOptions(), useOptionals: true },
-        expected: code`string`,
+        expected: code`string | undefined`,
       },
       {
         descr: 'value types (useOptionals="all")',
         typeMap: new Map(),
         protoType: ".google.protobuf.StringValue",
         options: { ...defaultOptions(), useOptionals: "all" },
-        expected: code`string`,
+        expected: code`string | undefined`,
       },
     ];
     testCases.forEach((t) =>