Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add padStart and padEnd methods #31

Merged
merged 1 commit into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ npm install string-ts
- [endsWith](#endsWith)
- [join](#join)
- [length](#length)
- [padEnd](#padend)
- [padStart](#padstart)
- [repeat](#repeat)
- [replace](#replace)
- [replaceAll](#replaceall)
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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'
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export type {
EndsWith,
Join,
Length,
PadEnd,
PadStart,
Repeat,
Replace,
ReplaceAll,
Expand All @@ -21,6 +23,8 @@ export {
endsWith,
join,
length,
padEnd,
padStart,
repeat,
replace,
replaceAll,
Expand Down
8 changes: 4 additions & 4 deletions src/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ namespace Math {
B extends number,
> = TupleOf<A> extends [...infer U, ...TupleOf<B>] ? U['length'] : 0

export type IsPositive<T extends number> = `${T}` extends `-${number}`
? false
: true
export type IsNegative<T extends number> = `${T}` extends `-${number}`
? true
: false

export type Abs<T extends number> =
`${T}` extends `-${infer U extends number}` ? U : T

export type GetPositiveIndex<
T extends string,
I extends number,
> = IsPositive<I> extends true ? I : Subtract<Length<T>, Abs<I>>
> = IsNegative<I> extends false ? I : Subtract<Length<T>, Abs<I>>
}

export type { Math }
88 changes: 81 additions & 7 deletions src/primitives.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Equal<typeof result, 'hello '>>
})

test('should pad with a given string', () => {
const data = 'hello'
const result = subject.padEnd(data, 10, '=>')
expect(result).toEqual('hello=>=>=')
type test = Expect<Equal<typeof result, 'hello=>=>='>>
})

test('should not pad if no arguments are given', () => {
const data = 'hello'
const result = subject.padEnd(data)
expect(result).toEqual('hello')
type test = Expect<Equal<typeof result, 'hello'>>
})

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<Equal<typeof result, 'hello'>>
})

test('should not pad for negative numbers', () => {
const data = 'hello'
const result = subject.padEnd(data, -1, '=')
expect(result).toEqual('hello')
type test = Expect<Equal<typeof result, 'hello'>>
})
})

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<Equal<typeof result, ' hello'>>
})

test('should pad with a given string', () => {
const data = 'hello'
const result = subject.padStart(data, 10, '=>')
expect(result).toEqual('=>=>=hello')
type test = Expect<Equal<typeof result, '=>=>=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<Equal<typeof result, 'hello'>>
})

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<Equal<typeof result, 'hello'>>
})

test('should not pad for negative numbers', () => {
const data = 'hello'
const result = subject.padStart(data, -1, '=')
expect(result).toEqual('hello')
type test = Expect<Equal<typeof result, 'hello'>>
})
})

describe('repeat', () => {
test('should repeat the string by a given number of times', () => {
const data = 'abc'
const result = subject.repeat(data, 3)
Expand All @@ -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, ' ')
Expand All @@ -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, ' ')
Expand Down Expand Up @@ -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, ' ')
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
76 changes: 71 additions & 5 deletions src/primitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ type EndsWith<
T extends string,
S extends string,
P extends number = Length<T>,
> = Math.IsPositive<P> extends true
> = Math.IsNegative<P> extends false
? P extends Length<T>
? S extends Slice<T, Math.Subtract<Length<T>, Length<S>>, Length<T>>
? true
Expand Down Expand Up @@ -118,15 +118,77 @@ function length<T extends string>(str: T) {
return str.length as Length<T>
}

/**
* 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<times> extends false
? Math.Subtract<times, Length<T>> extends infer missing extends number
? `${T}${Slice<Repeat<pad, missing>, 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<T extends string, N extends number = 0, U extends string = ' '>(
str: T,
length: N = 0 as N,
pad: U = ' ' as U,
) {
return str.padEnd(length, pad) as PadEnd<T, N, U>
}

/**
* 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<times> extends false
? Math.Subtract<times, Length<T>> extends infer missing extends number
? `${Slice<Repeat<pad, missing>, 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<T, N, U>
}

/**
* Repeats a string N times.
* T: The string to repeat.
* N: The number of times to repeat.
*/
type Repeat<T extends string, N extends number = 0> = N extends 0
type Repeat<T extends string, times extends number = 0> = times extends 0
? ''
: Math.IsPositive<N> extends true
? Join<TupleOf<N, T>>
: Math.IsNegative<times> extends false
? Join<TupleOf<times, T>>
: never
/**
* A strongly-typed version of `String.prototype.repeat`.
Expand Down Expand Up @@ -286,7 +348,7 @@ type StartsWith<
T extends string,
S extends string,
P extends number = 0,
> = Math.IsPositive<P> extends true
> = Math.IsNegative<P> extends false
? P extends 0
? T extends `${S}${string}`
? true
Expand Down Expand Up @@ -364,6 +426,8 @@ export type {
EndsWith,
Join,
Length,
PadEnd,
PadStart,
Repeat,
Replace,
ReplaceAll,
Expand All @@ -380,6 +444,8 @@ export {
endsWith,
join,
length,
padEnd,
padStart,
repeat,
replace,
replaceAll,
Expand Down