diff --git a/README.md b/README.md index 0972260..dea0e7e 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ npm install string-ts - [endsWith](#endsWith) - [join](#join) - [length](#length) + - [padEnd](#padend) + - [padStart](#padstart) - [repeat](#repeat) - [replace](#replace) - [replaceAll](#replaceall) @@ -225,6 +227,30 @@ const result = length(str) // ^ 5 ``` +### padEnd + +This function is a strongly-typed counterpart of `String.prototype.padEnd`. + +```ts +import { padEnd } from 'string-ts' + +const str = 'hello' +const result = padEnd(str, 10, '=') +// ^ 'hello=====' +``` + +### padStart + +This function is a strongly-typed counterpart of `String.prototype.padStart`. + +```ts +import { padStart } from 'string-ts' + +const str = 'hello' +const result = padStart(str, 10, '=') +// ^ '=====hello' +``` + ### repeat This function is a strongly-typed counterpart of `String.prototype.repeat`. @@ -683,6 +709,8 @@ St.Concat<['a', 'bc', 'def']> // 'abcdef' St.EndsWith<'abc', 'c'> // true St.Join<['hello', 'world'], '-'> // 'hello-world' St.Length<'hello'> // 5 +St.PadEnd<'hello', 10, '='> // 'hello=====' +St.PadStart<'hello', 10, '='> // '=====hello' St.Repeat<'abc', 3> // 'abcabcabc' St.Replace<'hello-world', 'l', '1'> // 'he1lo-world' St.ReplaceAll<'hello-world', 'l', '1'> // 'he11o-wor1d' diff --git a/src/index.ts b/src/index.ts index 66e9d13..cc56e8e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,8 @@ export type { EndsWith, Join, Length, + PadEnd, + PadStart, Repeat, Replace, ReplaceAll, @@ -21,6 +23,8 @@ export { endsWith, join, length, + padEnd, + padStart, repeat, replace, replaceAll, diff --git a/src/math.ts b/src/math.ts index 4f70b4f..e6babae 100644 --- a/src/math.ts +++ b/src/math.ts @@ -7,9 +7,9 @@ namespace Math { B extends number, > = TupleOf extends [...infer U, ...TupleOf] ? U['length'] : 0 - export type IsPositive = `${T}` extends `-${number}` - ? false - : true + export type IsNegative = `${T}` extends `-${number}` + ? true + : false export type Abs = `${T}` extends `-${infer U extends number}` ? U : T @@ -17,7 +17,7 @@ namespace Math { export type GetPositiveIndex< T extends string, I extends number, - > = IsPositive extends true ? I : Subtract, Abs> + > = IsNegative extends false ? I : Subtract, Abs> } export type { Math } diff --git a/src/primitives.test.ts b/src/primitives.test.ts index 1176115..32c367b 100644 --- a/src/primitives.test.ts +++ b/src/primitives.test.ts @@ -79,7 +79,81 @@ describe('primitives', () => { }) }) - test('repeat', () => { + describe('padEnd', () => { + test('should pad a string at the end', () => { + const data = 'hello' + const result = subject.padEnd(data, 10) + expect(result).toEqual('hello ') + type test = Expect> + }) + + test('should pad with a given string', () => { + const data = 'hello' + const result = subject.padEnd(data, 10, '=>') + expect(result).toEqual('hello=>=>=') + type test = Expect=>='>> + }) + + test('should not pad if no arguments are given', () => { + const data = 'hello' + const result = subject.padEnd(data) + expect(result).toEqual('hello') + type test = Expect> + }) + + test('should not pad or truncate if length is shorter than string', () => { + const data = 'hello' + const result = subject.padEnd(data, 3, '=') + expect(result).toEqual('hello') + type test = Expect> + }) + + test('should not pad for negative numbers', () => { + const data = 'hello' + const result = subject.padEnd(data, -1, '=') + expect(result).toEqual('hello') + type test = Expect> + }) + }) + + describe('padStart', () => { + test('should pad a string at the start', () => { + const data = 'hello' + const result = subject.padStart(data, 10) + expect(result).toEqual(' hello') + type test = Expect> + }) + + test('should pad with a given string', () => { + const data = 'hello' + const result = subject.padStart(data, 10, '=>') + expect(result).toEqual('=>=>=hello') + type test = Expect=>=hello'>> + }) + + test('should not pad if no arguments are given', () => { + const data = 'hello' + const result = subject.padStart(data) + expect(result).toEqual('hello') + type test = Expect> + }) + + test('should not pad or truncate if length is shorter than string', () => { + const data = 'hello' + const result = subject.padStart(data, 3, '=') + expect(result).toEqual('hello') + type test = Expect> + }) + + test('should not pad for negative numbers', () => { + const data = 'hello' + const result = subject.padStart(data, -1, '=') + expect(result).toEqual('hello') + type test = Expect> + }) + }) + + describe('repeat', () => { test('should repeat the string by a given number of times', () => { const data = 'abc' const result = subject.repeat(data, 3) @@ -101,7 +175,7 @@ describe('primitives', () => { }) }) - test('replace', () => { + describe('replace', () => { test('should replace chars in a string at both type level and runtime level once', () => { const data = 'some nice string' const result = subject.replace(data, ' ') @@ -110,7 +184,7 @@ describe('primitives', () => { }) }) - test('replaceAll', () => { + describe('replaceAll', () => { test('should replace all chars in a string at both type level and runtime level once', () => { const data = 'some nice string' const result = subject.replaceAll(data, ' ') @@ -190,7 +264,7 @@ describe('primitives', () => { }) }) - test('split', () => { + describe('split', () => { test('should split a string by a delimiter into an array of substrings', () => { const data = 'some nice string' const result = subject.split(data, ' ') @@ -296,7 +370,7 @@ describe('primitives', () => { }) }) - test('trimStart', () => { + describe('trimStart', () => { test('should trim the start of a string at both type level and runtime level', () => { const data = ' some nice string ' const result = subject.trimStart(data) @@ -305,7 +379,7 @@ describe('primitives', () => { }) }) - test('trimEnd', () => { + describe('trimEnd', () => { test('should trim the end of a string at both type level and runtime level', () => { const data = ' some nice string ' const result = subject.trimEnd(data) @@ -314,7 +388,7 @@ describe('primitives', () => { }) }) - test('trim', () => { + describe('trim', () => { test('should trim a string at both type level and runtime level', () => { const data = ' some nice string ' const result = subject.trim(data) diff --git a/src/primitives.ts b/src/primitives.ts index f002c87..d8fbf75 100644 --- a/src/primitives.ts +++ b/src/primitives.ts @@ -47,7 +47,7 @@ type EndsWith< T extends string, S extends string, P extends number = Length, -> = Math.IsPositive

extends true +> = Math.IsNegative

extends false ? P extends Length ? S extends Slice, Length>, Length> ? true @@ -118,15 +118,77 @@ function length(str: T) { return str.length as Length } +/** + * Pads a string at the end with another string. + * T: The string to pad. + * times: The number of times to pad. + * pad: The string to pad with. + */ +type PadEnd< + T extends string, + times extends number = 0, + pad extends string = ' ', +> = Math.IsNegative extends false + ? Math.Subtract> extends infer missing extends number + ? `${T}${Slice, 0, missing>}` + : never + : T +/** + * A strongly-typed version of `String.prototype.padEnd`. + * @param str the string to pad. + * @param length the length to pad. + * @param pad the string to pad with. + * @returns the padded string in both type level and runtime. + * @example padEnd('hello', 10, '=') // 'hello=====' + */ +function padEnd( + str: T, + length: N = 0 as N, + pad: U = ' ' as U, +) { + return str.padEnd(length, pad) as PadEnd +} + +/** + * Pads a string at the start with another string. + * T: The string to pad. + * times: The number of times to pad. + * pad: The string to pad with. + */ +type PadStart< + T extends string, + times extends number = 0, + pad extends string = ' ', +> = Math.IsNegative extends false + ? Math.Subtract> extends infer missing extends number + ? `${Slice, 0, missing>}${T}` + : never + : T +/** + * A strongly-typed version of `String.prototype.padStart`. + * @param str the string to pad. + * @param length the length to pad. + * @param pad the string to pad with. + * @returns the padded string in both type level and runtime. + * @example padStart('hello', 10, '=') // '=====hello' + */ +function padStart< + T extends string, + N extends number = 0, + U extends string = ' ', +>(str: T, length: N = 0 as N, pad: U = ' ' as U) { + return str.padStart(length, pad) as PadStart +} + /** * Repeats a string N times. * T: The string to repeat. * N: The number of times to repeat. */ -type Repeat = N extends 0 +type Repeat = times extends 0 ? '' - : Math.IsPositive extends true - ? Join> + : Math.IsNegative extends false + ? Join> : never /** * A strongly-typed version of `String.prototype.repeat`. @@ -286,7 +348,7 @@ type StartsWith< T extends string, S extends string, P extends number = 0, -> = Math.IsPositive

extends true +> = Math.IsNegative

extends false ? P extends 0 ? T extends `${S}${string}` ? true @@ -364,6 +426,8 @@ export type { EndsWith, Join, Length, + PadEnd, + PadStart, Repeat, Replace, ReplaceAll, @@ -380,6 +444,8 @@ export { endsWith, join, length, + padEnd, + padStart, repeat, replace, replaceAll,