Skip to content

Commit

Permalink
feat(utils): includes
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed May 18, 2023
1 parent e0b3571 commit c490266
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 11 deletions.
46 changes: 46 additions & 0 deletions src/utils/__tests__/includes.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* @file Unit Tests - includes
* @module tutils/utils/tests/unit/includes
*/

import testSubject from '../includes'

describe('unit:utils/includes', () => {
it('should return false if value does not include target', () => {
// Arrange
const cases: Parameters<typeof testSubject>[] = [
['abc', 'z'],
['abc', 'a', null, 1],
[['a', 'b', 'c'], 'z'],
[['a', 'b', 'c'], 'a', undefined, 1]
]

// Act + Expect
cases.forEach(([value, target, identity, position]) => {
expect(testSubject(value, target, identity, position)).to.be.false
})
})

it('should return true if value includes target', () => {
// Arrange
const cases: Parameters<typeof testSubject>[] = [
['foobar', 'foo'],
['foobar', 'bar', null, 3],
[[faker.number.int(), { x: 0, y: 0 }], { x: 0, y: 0 }, undefined, 1],
[
[faker.number.int(), { x: 0, y: 0 }],
{ x: 0, y: 0 },
item => {
const { x, y = Number.NaN } = item as { x: number; y?: number }
return `[${x},${y}]`
},
1
]
]

// Act + Expect
cases.forEach(([value, target, identity, position]) => {
expect(testSubject(value, target, identity, position)).to.be.true
})
})
})
51 changes: 51 additions & 0 deletions src/utils/includes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @file Utilities - includes
* @module tutils/utils/includes
*/

import type {
Fn,
IfString,
IndexSignature,
NIL,
Nilable,
NumberString
} from '#src/types'
import equal from './equal'
import isString from './is-string'

/**
* Checks if an array or string `value` includes `target`.
*
* An array `value` includes `target` if `target` is deeply equal to at least
* one item in the array. A string `value` includes `target` if `target` is
* equal to at least one character in the string.
*
* When searching an array, an `identity` function can be used to convert array
* items to unique keys. If provided, two items with the same identity key will
* be considered equal.
*
* @template T - Value to search
* @template K - Identity key type
*
* @param {T} value - Value to search
* @param {unknown} target - Search target
* @param {Nilable<Fn<[T[0]], K>>} [identity] - Identity key function
* @param {number | undefined} [position=0] - Position to begin search
* @return {boolean} `true` if `data` includes `target`
*/
function includes<
T extends string | readonly unknown[],
K extends IndexSignature = NumberString
>(
value: T,
target: unknown,
identity?: IfString<T, NIL, Nilable<Fn<[T[0]], K>>>,
position: number | undefined = 0
): boolean {
return isString(value)
? value.includes(target as string, position)
: value.slice(position).some(item => equal(target, item, identity))
}

export default includes
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

export { default as capitalize } from './capitalize'
export { default as equal } from './equal'
export { default as includes } from './includes'
export { default as isAppEnv } from './is-app-env'
export { default as isArray } from './is-array'
export { default as isBigInt } from './is-big-int'
Expand Down
7 changes: 4 additions & 3 deletions src/utils/is-app-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
*/

import AppEnv from '#src/enums/app-env'
import includes from './includes'

/**
* Checks if the given `value` is a valid app environment.
* Checks if `value` is a valid app environment.
*
* @param {unknown} value - Value to evaluate
* @param {unknown} value - Value to check
* @return {value is AppEnv} `true` if `value` is {@linkcode AppEnv}
*/
const isAppEnv = (value: unknown): value is AppEnv => {
return Object.values(AppEnv).includes(value as AppEnv)
return includes(Object.values(AppEnv), value)
}

export default isAppEnv
7 changes: 4 additions & 3 deletions src/utils/is-booleanish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
*/

import type { Booleanish } from '#src/types'
import includes from './includes'
import isBoolean from './is-boolean'

/**
* Checks if the given `value` is a boolean, `'false'`, or `'true'`.
* Checks if `value` is a boolean, `'false'`, or `'true'`.
*
* @param {unknown} value - Value to evaluate
* @param {unknown} value - Value to check
* @return {value is Booleanish} `true` if `value` is {@linkcode Booleanish}
*/
const isBooleanish = (value: unknown): value is Booleanish => {
return isBoolean(value) || value === 'false' || value === 'true'
return isBoolean(value) || includes(['false', 'true'], value)
}

export default isBooleanish
7 changes: 4 additions & 3 deletions src/utils/is-node-env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
*/

import NodeEnv from '#src/enums/node-env'
import includes from './includes'

/**
* Checks if the given `value` is a valid node environment.
* Checks if `value` is a valid node environment.
*
* @param {unknown} [value=process.env.NODE_ENV] - Value to evaluate
* @param {unknown} [value=process.env.NODE_ENV] - Value to check
* @return {value is NodeEnv} `true` if `value` is {@linkcode NodeEnv}
*/
const isNodeEnv = (value: unknown = process.env.NODE_ENV): value is NodeEnv => {
return Object.values(NodeEnv).includes(value as NodeEnv)
return includes(Object.values(NodeEnv), value)
}

export default isNodeEnv
4 changes: 2 additions & 2 deletions src/utils/is-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
*/

/**
* Checks if the given `value` is a string.
* Checks if `value` is a string.
*
* @param {unknown} value - Value to evaluate
* @param {unknown} value - Value to check
* @return {value is string} `true` if `value` is a string
*/
const isString = (value: unknown): value is string => typeof value === 'string'
Expand Down

0 comments on commit c490266

Please sign in to comment.