Commit ac2f1dd

Anton Golub <antongolub@antongolub.com>
2024-10-04 16:08:48
refactor: enhance `Shell` result type (#915)
1 parent 1384a8c
Changed files (4)
src/core.ts
@@ -115,17 +115,21 @@ export const defaults: Options = {
   timeoutSignal: 'SIGTERM',
 }
 
-export interface Shell {
-  (pieces: TemplateStringsArray, ...args: any[]): ProcessPromise
-  (opts: Partial<Options>): Shell
+// prettier-ignore
+export interface Shell<
+  S = false,
+  R = S extends true ? ProcessOutput : ProcessPromise,
+> {
+  (pieces: TemplateStringsArray, ...args: any[]): R
+  <O extends Partial<Options> = Partial<Options>, R = O extends { sync: true } ? Shell<true> : Shell>(opts: O): R
   sync: {
     (pieces: TemplateStringsArray, ...args: any[]): ProcessOutput
-    (opts: Partial<Options>): Shell
+    (opts: Partial<Omit<Options, 'sync'>>): Shell<true>
   }
 }
 
 export const $: Shell & Options = new Proxy<Shell & Options>(
-  function (pieces, ...args) {
+  function (pieces: TemplateStringsArray | Partial<Options>, ...args: any) {
     const snapshot = getStore()
     if (!Array.isArray(pieces)) {
       return function (this: any, ...args: any) {
@@ -136,9 +140,9 @@ export const $: Shell & Options = new Proxy<Shell & Options>(
       }
     }
     const from = getCallerLocation()
-    if (pieces.some((p) => p == undefined)) {
+    if (pieces.some((p) => p == undefined))
       throw new Error(`Malformed command at ${from}`)
-    }
+
     checkShell()
     checkQuote()
 
test/smoke/ts.test.ts
@@ -15,12 +15,18 @@
 import * as assert from 'node:assert'
 import 'zx/globals'
 ;(async () => {
-  // smoke test
+  // smoke test async
   {
     const p = await $`echo foo`
     assert.match(p.stdout, /foo/)
   }
 
+  // smoke test sync
+  {
+    const p = $.sync`echo foo`
+    assert.match(p.stdout, /foo/)
+  }
+
   // captures err stack
   {
     const p = await $({ nothrow: true })`echo foo; exit 3`
test-d/core.test-d.ts
@@ -56,3 +56,8 @@ expectType<ProcessOutput>(new ProcessOutput(null, null, '', '', '', ''))
 expectError(new ProcessOutput(null, null))
 
 expectType<'banana'>(within(() => 'apple' as 'banana'))
+
+expectType<ProcessPromise>($`cmd`)
+expectType<ProcessPromise>($({ sync: false })`cmd`)
+expectType<ProcessOutput>($({ sync: true })`cmd`)
+expectType<ProcessOutput>($.sync`cmd`)
.size-limit.json
@@ -30,7 +30,7 @@
   {
     "name": "all",
     "path": "build/*",
-    "limit": "832 kB",
+    "limit": "833 kB",
     "brotli": false,
     "gzip": false
   }