Repository: remeda/remeda
Pull request #460 (2024-01-12 00:44:35)

feat: Adds isSymbol function
Adds an `isSymbol` type guard function.  Addresses #456 

---

Make sure that you:

- [x] Typedoc added for new methods and updated for changed
- [x] Tests added for new methods and updated for changed
- [x] New methods added to `src/index.ts`
- [x] New methods added to `mapping.md`




viveleroi: Note: This is PR is incomplete as somethings keeps overwriting isDefined/isNotNull types on commit which causes tsc failures and I'm not immediately sure what.
codesandbox-ci[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/remeda/remeda/pr/460/builds/459356) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 327669fa5dca5b2606d0745558e2030bf34ad651:

|Sandbox| Source |
|--|--|
|[remeda-example-vanilla](https://codesandbox.io/s/remeda-example-vanilla-rll7ml)| Configuration |

viveleroi: I would appreciate some input on what's triggering the automatic writing of the files I need to modify.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/remeda/remeda/pull/460?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`d371a72`)](https://app.codecov.io/gh/remeda/remeda/commit/d371a721f74ed3a72ba2186823cb73675fa07d06?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda) 98.39% compared to head [(`732c292`)](https://app.codecov.io/gh/remeda/remeda/pull/460?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda) 98.40%.


<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##           master     #460   +/-   ##
=======================================
  Coverage   98.39%   98.40%           
=======================================
  Files         124      125    +1     
  Lines        7858     7878   +20     
  Branches      717      718    +1     
=======================================
+ Hits         7732     7752   +20     
  Misses        123      123           
  Partials        3        3           
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/remeda/remeda/pull/460?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=remeda).


Patch:
diff --git a/docs/package-lock.json b/docs/package-lock.json
index 8c78760..b43fd16 100644
--- a/docs/package-lock.json
+++ b/docs/package-lock.json
@@ -41,7 +41,7 @@
         "type-fest": "^4.9.0",
         "typedoc": "^0.25.5",
         "typescript": "^5.3.3",
-        "vite": "^5.0.10"
+        "vite": "^5.0.12"
       }
     },
     "node_modules/@aashutoshrathi/word-wrap": {
@@ -5793,9 +5793,9 @@
       "dev": true
     },
     "node_modules/vite": {
-      "version": "5.0.10",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz",
-      "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==",
+      "version": "5.0.12",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
+      "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.19.3",
diff --git a/docs/package.json b/docs/package.json
index 73c9eef..bdb0017 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -48,6 +48,6 @@
     "type-fest": "^4.9.0",
     "typedoc": "^0.25.5",
     "typescript": "^5.3.3",
-    "vite": "^5.0.10"
+    "vite": "^5.0.12"
   }
 }
diff --git a/mapping.md b/mapping.md
index 0ac4788..db83649 100644
--- a/mapping.md
+++ b/mapping.md
@@ -47,6 +47,7 @@ documentation when migrating._
 | `invert`            | `invert`            | `invertObj`         |
 | `isEmpty`           | `isEmpty`           | `isEmpty`           |
 | `isNil`             | `isNil`             | `isNil`             |
+| `isSymbol`          | `isSymbol`          |                     |
 | `join`              | `join`              | `join`              |
 | `last`              | `last`              | `last`              |
 | `length`            | `size`              | `length`            |
diff --git a/package-lock.json b/package-lock.json
index 5ce598f..baf80a4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8869,9 +8869,9 @@
       }
     },
     "node_modules/vite": {
-      "version": "5.0.10",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz",
-      "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==",
+      "version": "5.0.12",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz",
+      "integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==",
       "dev": true,
       "dependencies": {
         "esbuild": "^0.19.3",
diff --git a/src/index.ts b/src/index.ts
index 7925fd1..cbc5246 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -53,6 +53,7 @@ export * from './isNot';
 export * from './isNumber';
 export * from './isObject';
 export * from './isPromise';
+export * from './isSymbol';
 export * from './isString';
 export * from './isTruthy';
 export * from './join';
diff --git a/src/isSymbol.ts b/src/isSymbol.ts
new file mode 100644
index 0000000..6bd1110
--- /dev/null
+++ b/src/isSymbol.ts
@@ -0,0 +1,20 @@
+type DefinitelySymbol<T> = Extract<T, string> extends never
+  ? symbol
+  : Extract<T, symbol> extends any
+    ? symbol
+    : Extract<T, symbol>;
+
+/**
+ * A function that checks if the passed parameter is a symbol and narrows its type accordingly
+ * @param data the variable to check
+ * @signature
+ *    R.isSymbol(data)
+ * @returns true if the passed input is a symbol, false otherwise
+ * @example
+ *    R.isSymbol(Symbol('foo')) //=> true
+ *    R.isSymbol(1) //=> false
+ * @category Guard
+ */
+export function isSymbol<T>(data: T | symbol): data is DefinitelySymbol<T> {
+  return typeof data === 'symbol';
+}


Test patch:
diff --git a/src/isDefined.test.ts b/src/isDefined.test.ts
index 6d83f16..17b76f8 100644
--- a/src/isDefined.test.ts
+++ b/src/isDefined.test.ts
@@ -16,6 +16,7 @@ describe('isDefined', () => {
         | Error
         | number
         | Promise<number>
+        | symbol
       >(data);
     }
   });
@@ -41,6 +42,7 @@ describe('isDefined', () => {
         | Date
         | Error
         | Promise<number>
+        | symbol
       >
     >(data);
   });
@@ -62,6 +64,7 @@ describe('strict', () => {
         | number
         | Promise<number>
         | null
+        | symbol
       >(data);
     }
   });
@@ -89,6 +92,7 @@ describe('strict', () => {
         | Error
         | Promise<number>
         | null
+        | symbol
       >
     >(data);
   });
diff --git a/src/isNonNull.test.ts b/src/isNonNull.test.ts
index c59bf7e..923b231 100644
--- a/src/isNonNull.test.ts
+++ b/src/isNonNull.test.ts
@@ -17,6 +17,7 @@ describe('isNonNull', () => {
         | number
         | Promise<number>
         | undefined
+        | symbol
       >(data);
     }
   });
@@ -44,6 +45,7 @@ describe('isNonNull', () => {
         | Error
         | Promise<number>
         | undefined
+        | symbol
       >
     >(data);
   });
diff --git a/src/isNot.test.ts b/src/isNot.test.ts
index 4fa7bbe..87e9bb0 100644
--- a/src/isNot.test.ts
+++ b/src/isNot.test.ts
@@ -19,6 +19,7 @@ describe('isNot', () => {
         | Date
         | Error
         | Promise<number>
+        | symbol
         | null
         | undefined
       >(data);
@@ -46,6 +47,7 @@ describe('isNot', () => {
         | null
         | Error
         | number
+        | symbol
       >
     >(result);
   });
diff --git a/src/isSymbol.test.ts b/src/isSymbol.test.ts
new file mode 100644
index 0000000..37b3f77
--- /dev/null
+++ b/src/isSymbol.test.ts
@@ -0,0 +1,32 @@
+import { isSymbol } from './isSymbol';
+import { typesDataProvider } from '../test/types_data_provider';
+
+describe('isSymbol', () => {
+  test('isSymbol: should work as type guard', () => {
+    const data = typesDataProvider('symbol');
+    if (isSymbol(data)) {
+      expect(typeof data).toEqual('symbol');
+      assertType<symbol>(data);
+    }
+  });
+  test('isSymbol: should work even if data type is unknown', () => {
+    const data: unknown = typesDataProvider('symbol');
+    if (isSymbol(data)) {
+      expect(typeof data).toEqual('symbol');
+      assertType<symbol>(data);
+    }
+  });
+  test('isSymbol: should work as type guard in array', () => {
+    const data = [
+      typesDataProvider('symbol'),
+      typesDataProvider('error'),
+      typesDataProvider('string'),
+      typesDataProvider('function'),
+      typesDataProvider('null'),
+      typesDataProvider('array'),
+      typesDataProvider('boolean'),
+    ].filter(isSymbol);
+    expect(data.every(c => typeof c === 'symbol')).toEqual(true);
+    assertType<Array<symbol>>(data);
+  });
+});
diff --git a/test/types_data_provider.ts b/test/types_data_provider.ts
index 8aad653..527b1c5 100644
--- a/test/types_data_provider.ts
+++ b/test/types_data_provider.ts
@@ -9,7 +9,8 @@ type TestObj =
   | null
   | Error
   | number
-  | Promise<number>;
+  | Promise<number>
+  | symbol;
 
 export const typesDataProvider = (
   t:
@@ -24,6 +25,7 @@ export const typesDataProvider = (
     | 'error'
     | 'number'
     | 'promise'
+    | 'symbol'
 ): TestObj => {
   switch (t) {
     case 'number':
@@ -48,6 +50,8 @@ export const typesDataProvider = (
       return { a: 'asd' };
     case 'error':
       return new Error('asd');
+    case 'symbol':
+      return Symbol('symbol');
     case 'undefined':
       return undefined;
   }