diff --git a/readme.md b/readme.md index df7a393e7..34b034cc4 100644 --- a/readme.md +++ b/readme.md @@ -136,6 +136,7 @@ Click the type names for complete docs. - [`SnakeCase`](ts41/snake-case.d.ts) – Convert a string literal to snake-case (`foo_bar`). - [`SnakeCasedProperties`](ts41/snake-cased-properties-deep.d.ts) – Convert object properties to snake-case (`foo_bar`). - [`SnakeCasedPropertiesDeep`](ts41/snake-cased-properties-deep.d.ts) – Convert object properties to snake-case recursively (`foo_bar`). +- [`ScreamingSnakeCase`](ts41/screaming-snake-case.d.ts) - Convert a string literal to screaming-snake-case (`FOO_BAR`). - [`DelimiterCase`](ts41/delimiter-case.d.ts) – Convert a string literal to a custom string delimiter casing. - [`DelimiterCasedProperties`](ts41/delimiter-cased-properties.d.ts) – Convert object properties to a custom string delimiter casing. - [`DelimiterCasedPropertiesDeep`](ts41/delimiter-cased-properties-deep.d.ts) – Convert object properties to a custom string delimiter casing recursively. diff --git a/test-d/screaming-snake-case.ts b/test-d/screaming-snake-case.ts new file mode 100644 index 000000000..79e93daed --- /dev/null +++ b/test-d/screaming-snake-case.ts @@ -0,0 +1,23 @@ +import {ScreamingSnakeCase} from '../ts41/screaming-snake-case'; +import {expectType} from 'tsd'; + +const screamingSnakeFromCamel: ScreamingSnakeCase<'fooBar'> = 'FOO_BAR'; +expectType<'FOO_BAR'>(screamingSnakeFromCamel); + +const screamingSnakeFromKebab: ScreamingSnakeCase<'foo-bar'> = 'FOO_BAR'; +expectType<'FOO_BAR'>(screamingSnakeFromKebab); + +const screamingSnakeFromSpace: ScreamingSnakeCase<'foo bar'> = 'FOO_BAR'; +expectType<'FOO_BAR'>(screamingSnakeFromSpace); + +const screamingSnakeFromSnake: ScreamingSnakeCase<'foo_bar'> = 'FOO_BAR'; +expectType<'FOO_BAR'>(screamingSnakeFromSnake); + +const screamingSnakeFromScreamingSnake: ScreamingSnakeCase<'FOO_BAR'> = 'FOO_BAR'; +expectType<'FOO_BAR'>(screamingSnakeFromScreamingSnake); + +const noScreamingSnakeFromMono: ScreamingSnakeCase<'foobar'> = 'FOOBAR'; +expectType<'FOOBAR'>(noScreamingSnakeFromMono); + +const nonStringFromNonString: ScreamingSnakeCase<[]> = []; +expectType<[]>(nonStringFromNonString); diff --git a/ts41/index.d.ts b/ts41/index.d.ts index ba4f4640e..a358f9c54 100644 --- a/ts41/index.d.ts +++ b/ts41/index.d.ts @@ -14,6 +14,7 @@ export {PascalCasedPropertiesDeep} from './pascal-cased-properties-deep'; export {SnakeCase} from './snake-case'; export {SnakeCasedProperties} from './snake-cased-properties'; export {SnakeCasedPropertiesDeep} from './snake-cased-properties-deep'; +export {ScreamingSnakeCase} from './screaming-snake-case'; export {DelimiterCase} from './delimiter-case'; export {DelimiterCasedProperties} from './delimiter-cased-properties'; export {DelimiterCasedPropertiesDeep} from './delimiter-cased-properties-deep'; diff --git a/ts41/screaming-snake-case.d.ts b/ts41/screaming-snake-case.d.ts new file mode 100644 index 000000000..1a8c44c99 --- /dev/null +++ b/ts41/screaming-snake-case.d.ts @@ -0,0 +1,40 @@ +import {SplitIncludingDelimiters} from './delimiter-case'; +import {SnakeCase} from './snake-case'; + +/** +Returns a boolean for whether the given array includes the given item. +*/ +type Includes = { + [P in keyof Value & number as Value[P]]: true; +}[Item] extends true + ? true + : false; + +/** +Returns a boolean for whether the string is screaming snake case. +*/ +type IsScreamingSnakeCase = Value extends Uppercase + ? Includes, '_'>, '_'> extends true + ? true + : false + : false; + +/** +Convert a string literal to screaming-snake-case. + +This can be useful when, for example, converting a camel-cased object property to a screaming-snake-cased SQL column name. + +@example +``` +import {ScreamingSnakeCase} from 'type-fest'; + +const someVariable: ScreamingSnakeCase<'fooBar'> = 'FOO_BAR'; +``` + +@category Template Literals +*/ +export type ScreamingSnakeCase = Value extends string + ? IsScreamingSnakeCase extends true + ? Value + : Uppercase> + : Value;