From 919a29eef7d43c802efa214c3df8ad1d216120fb Mon Sep 17 00:00:00 2001 From: yutak23 <79501292+yutak23@users.noreply.github.com> Date: Wed, 18 Oct 2023 15:19:02 +0900 Subject: [PATCH] Fix camel-casing keys in nested objects when using union with null (#119) --- index.d.ts | 7 ++++--- index.test-d.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 1db6306..adb3473 100644 --- a/index.d.ts +++ b/index.d.ts @@ -3,7 +3,8 @@ import type {CamelCase, PascalCase} from 'type-fest'; // eslint-disable-next-line @typescript-eslint/ban-types type EmptyTuple = []; -type ObjectOptional = Record | undefined; +// Allow union with, for example, `undefined` and `null`. +type ObjectUnion = Record | unknown; /** Return a default type if input type is nil. @@ -38,7 +39,7 @@ type AppendPath = S extends '' Convert keys of an object to camelcase strings. */ export type CamelCaseKeys< - T extends ObjectOptional | readonly any[], + T extends ObjectUnion | readonly any[], Deep extends boolean = false, IsPascalCase extends boolean = false, PreserveConsecutiveUppercase extends boolean = false, @@ -71,7 +72,7 @@ export type CamelCaseKeys< ] ? T[P] : [Deep] extends [true] - ? T[P] extends ObjectOptional | readonly any[] + ? T[P] extends ObjectUnion | readonly any[] ? CamelCaseKeys< T[P], Deep, diff --git a/index.test-d.ts b/index.test-d.ts index 467d2b3..6cd710b 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -471,3 +471,16 @@ function camelcaseKeysPascalCase< expectType<{fooBar: {hogeHoge: string}}>(camelcaseKeysDeep({foo_bar: {hoge_hoge: 'hoge_hoge'}})); expectType<{FooBar: string}>(camelcaseKeysPascalCase({foo_bar: 'foo_bar'})); + +// Test for union type +// eslint-disable-next-line @typescript-eslint/ban-types +const objectCamelcased: CamelCaseKeys<{foo_bar: {foo_prop: string} | null}, true> + = camelcaseKeys({foo_bar: {foo_prop: 'foo_props'}}, {deep: true}); +// eslint-disable-next-line @typescript-eslint/ban-types +const nullCamelcased: CamelCaseKeys<{foo_bar: {foo_prop: string} | null}, true> + = camelcaseKeys({foo_bar: null}, {deep: true}); + +// eslint-disable-next-line @typescript-eslint/ban-types +expectType<{fooBar: {fooProp: string} | null}>(objectCamelcased); +// eslint-disable-next-line @typescript-eslint/ban-types +expectType<{fooBar: {fooProp: string} | null}>(nullCamelcased);