Skip to content

Commit

Permalink
require that inputs be plain objects (or arrays of) (#133)
Browse files Browse the repository at this point in the history
Closes #121
  • Loading branch information
yutak23 committed Mar 26, 2024
1 parent 8eb541c commit 313e5aa
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 9 deletions.
10 changes: 5 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ declare namespace snakecaseKeys {
@template Path - Path of keys.
*/
export type SnakeCaseKeys<
T extends ObjectUnion | readonly any[],
T extends ObjectUnion | ReadonlyArray<Record<string, unknown>>,
Deep extends boolean = true,
Exclude extends readonly unknown[] = EmptyTuple,
Path extends string = ""
> = T extends readonly any[]
> = T extends ReadonlyArray<Record<string, unknown>>
? // Handle arrays or tuples.
{
[P in keyof T]: T[P] extends Record<string, unknown> | readonly any[]
[P in keyof T]: T[P] extends Record<string, unknown> | ReadonlyArray<Record<string, unknown>>
? SnakeCaseKeys<T[P], Deep, Exclude>
: T[P];
}
Expand All @@ -64,7 +64,7 @@ declare namespace snakecaseKeys {
[P in keyof T as [Includes<Exclude, P>] extends [true]
? P
: SnakeCase<P>]: [Deep] extends [true]
? T[P] extends ObjectUnion | readonly any[]
? T[P] extends ObjectUnion | ReadonlyArray<Record<string, unknown>>
? SnakeCaseKeys<T[P], Deep, Exclude, AppendPath<Path, P & string>>
: T[P]
: T[P];
Expand Down Expand Up @@ -106,7 +106,7 @@ Convert object keys to snake using [`to-snake-case`](https://github.com/ianstorm
@param options - Options of conversion.
*/
declare function snakecaseKeys<
T extends Record<string, unknown> | readonly any[],
T extends Record<string, unknown> | ReadonlyArray<Record<string, unknown>>,
Options extends snakecaseKeys.Options
>(
input: T,
Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const map = require('map-obj')
const { snakeCase } = require('snake-case')

module.exports = function (obj, options) {
if (Array.isArray(obj) && obj.some(item => item.constructor !== Object)) { throw new Error('obj must be array of plain objects') }
if (obj.constructor !== Object) throw new Error('obj must be an plain object')

options = Object.assign({ deep: true, exclude: [], parsingOptions: {} }, options)

return map(obj, function (key, val) {
Expand Down
2 changes: 0 additions & 2 deletions index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ expectType<{ foo_bar: boolean }[]>(snakecaseKeys([{ fooBar: true }]));
expectAssignable<Array<{ [key: string]: boolean }>>(
snakecaseKeys([{ fooBar: true }])
);
expectType<string[]>(snakecaseKeys(["name 1", "name 2"]));

// Deep
expectType< { foo_bar: { "foo-bar": { "foo bar": true; }; }; nested: { pointObject: Point; }; }>(
Expand Down Expand Up @@ -114,7 +113,6 @@ expectAssignable<SnakeCaseKeys<{ [key: string]: boolean }>>(
const arrayInput = [{ fooBar: true }];
expectType<SnakeCaseKeys<typeof arrayInput>>(snakecaseKeys([{ fooBar: true }]));
expectAssignable<SnakeCaseKeys<typeof arrayInput>>(snakecaseKeys(arrayInput));
expectType<SnakeCaseKeys<string[]>>(snakecaseKeys(["name 1", "name 2"]));

// Deep
const deepInput = { foo_bar: { "foo-bar": { "foo bar": true } } };
Expand Down
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ snakecaseKeys({'foo-bar': true, nested: {fooBaz: 'bar'}});
##### obj

*Required*
Type: `object`
Type: object | array[object]`

An object to transform into snake case (keys only).
A plain object or array of plain objects to transform into snake case (keys only).

##### options

Expand Down
54 changes: 54 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,57 @@ test('shouldRecurse option', function (t) {
)
t.end()
})

test('not a plain object(primitive value)', function (t) {
t.throws(
() => Snake(1),
{ message: 'obj must be an plain object' },
'Should throw an error when input is not a plain object'
)
t.end()
})

test('not a plain object(function value)', function (t) {
t.throws(
() => Snake(() => { return 1 }),
{ message: 'obj must be an plain object' },
'Should throw an error when input is not a plain object'
)
t.end()
})

test('not a plain object(instance value)', function (t) {
t.throws(
() => Snake(new Date()),
{ message: 'obj must be an plain object' },
'Should throw an error when input is not a plain object'
)
t.end()
})

test('not array of plain objects(primitive value)', function (t) {
t.throws(
() => Snake([1, { fooBar: 'baz' }]),
{ message: 'obj must be array of plain objects' },
'Should throw an error when input is not an array of plain objects'
)
t.end()
})

test('not array of plain objects(function value)', function (t) {
t.throws(
() => Snake([() => { return 1 }, { fooBar: 'baz' }]),
{ message: 'obj must be array of plain objects' },
'Should throw an error when input is not an array of plain objects'
)
t.end()
})

test('not array of plain objects(instance value)', function (t) {
t.throws(
() => Snake([new Date(), { fooBar: 'baz' }]),
{ message: 'obj must be array of plain objects' },
'Should throw an error when input is not an array of plain objects'
)
t.end()
})

0 comments on commit 313e5aa

Please sign in to comment.