Skip to content

Commit

Permalink
handle unlikely edge case
Browse files Browse the repository at this point in the history
  • Loading branch information
wjhsf committed Feb 20, 2024
1 parent aa1ee67 commit 4fca950
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 3 deletions.
28 changes: 28 additions & 0 deletions lib/__tests__/util.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { safeToString } from '../utils'

describe('safeToString', () => {
const basic = [
undefined,
null,
true,
'string',
123,
321n,
{ object: 'yes' },
[1, 'hello', true, null],
(a: number, b: number) => a + b,
Symbol('safeToString'),
]
const testCases = [
...basic.map((input) => [input, String(input)]),
[Object.create(null), '[object Object]'],
[
[Object.create(null), Symbol('safeToString')],
'[object Object],Symbol("safeToString")',
],
]

it.each(testCases)('works on %s', (input, output) => {
expect(safeToString(input)).toBe(String(output))
})
})
10 changes: 7 additions & 3 deletions lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ export const objectToString = (obj: unknown) =>
Object.prototype.toString.call(obj)

/** Safely converts any value to string, using the value's own `toString` when available. */
export const safeToString = (val: unknown) => {
// Ideally, we'd just use String() for everything, but it breaks if `toString` is missing (mostly
// values with no prototype), so we have to use Object#toString as a fallback.
export const safeToString = (val: unknown): string => {
// Using .toString() fails for null/undefined and implicit conversion (val + "") fails for symbols
// and objects with null prototype
if (val === undefined || val === null || typeof val.toString === 'function') {
return String(val)
} else if (Array.isArray(val)) {
// Array#toString implicitly converts its values to strings, which is what we're trying to avoid
return val.map(safeToString).join()
} else {
// This case should just be objects with null prototype, so we can just use Object#toString
return objectToString(val)
}
}
Expand Down

0 comments on commit 4fca950

Please sign in to comment.