diff --git a/packages/stdlib/src/types/index.ts b/packages/stdlib/src/types/index.ts new file mode 100644 index 0000000..25d7c6a --- /dev/null +++ b/packages/stdlib/src/types/index.ts @@ -0,0 +1,2 @@ +export * from './js'; +export * from './ts'; \ No newline at end of file diff --git a/packages/stdlib/src/types/js/casts.test.ts b/packages/stdlib/src/types/js/casts.test.ts new file mode 100644 index 0000000..568a9c2 --- /dev/null +++ b/packages/stdlib/src/types/js/casts.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from 'vitest'; +import { toString } from './casts'; + +describe('casts', () => { + describe('toString', () => { + it('correct string representation of a value', () => { + // Primitives + expect(toString(true)).toBe('[object Boolean]'); + expect(toString(() => {})).toBe('[object Function]'); + expect(toString(5)).toBe('[object Number]'); + expect(toString(BigInt(5))).toBe('[object BigInt]'); + expect(toString('hello')).toBe('[object String]'); + expect(toString(Symbol('foo'))).toBe('[object Symbol]'); + + // Complex + expect(toString([])).toBe('[object Array]'); + expect(toString({})).toBe('[object Object]'); + expect(toString(undefined)).toBe('[object Undefined]'); + expect(toString(null)).toBe('[object Null]'); + expect(toString(/abc/)).toBe('[object RegExp]'); + expect(toString(new Date())).toBe('[object Date]'); + expect(toString(new Error())).toBe('[object Error]'); + expect(toString(new Promise(() => {}))).toBe('[object Promise]'); + expect(toString(new Map())).toBe('[object Map]'); + expect(toString(new Set())).toBe('[object Set]'); + expect(toString(new WeakMap())).toBe('[object WeakMap]'); + expect(toString(new WeakSet())).toBe('[object WeakSet]'); + }); + }); +}); \ No newline at end of file diff --git a/packages/stdlib/src/types/js/casts.ts b/packages/stdlib/src/types/js/casts.ts new file mode 100644 index 0000000..6190d84 --- /dev/null +++ b/packages/stdlib/src/types/js/casts.ts @@ -0,0 +1,7 @@ +/** + * To string any value. + * + * @param {any} value + * @returns {string} + */ +export const toString = (value: any): string => Object.prototype.toString.call(value); \ No newline at end of file diff --git a/packages/stdlib/src/types/js/complex.test.ts b/packages/stdlib/src/types/js/complex.test.ts new file mode 100644 index 0000000..ceff211 --- /dev/null +++ b/packages/stdlib/src/types/js/complex.test.ts @@ -0,0 +1,183 @@ +import { describe, expect, it } from 'vitest'; +import { isArray, isObject, isRegExp, isDate, isError, isPromise, isMap, isSet, isWeakMap, isWeakSet } from './complex'; + +describe('complex', () => { + describe('isArray', () => { + it('true if the value is an array', () => { + expect(isArray([])).toBe(true); + expect(isArray([1, 2, 3])).toBe(true); + }); + + it('false if the value is not an array', () => { + expect(isArray('')).toBe(false); + expect(isArray(123)).toBe(false); + expect(isArray(true)).toBe(false); + expect(isArray(null)).toBe(false); + expect(isArray(undefined)).toBe(false); + expect(isArray({})).toBe(false); + expect(isArray(new Map())).toBe(false); + expect(isArray(new Set())).toBe(false); + }); + }); + + describe('isObject', () => { + it('true if the value is an object', () => { + expect(isObject({})).toBe(true); + expect(isObject({ key: 'value' })).toBe(true); + }); + + it('false if the value is not an object', () => { + expect(isObject('')).toBe(false); + expect(isObject(123)).toBe(false); + expect(isObject(true)).toBe(false); + expect(isObject(null)).toBe(false); + expect(isObject(undefined)).toBe(false); + expect(isObject([])).toBe(false); + expect(isObject(new Map())).toBe(false); + expect(isObject(new Set())).toBe(false); + }); + }); + + describe('isRegExp', () => { + it('true if the value is a regexp', () => { + expect(isRegExp(/test/)).toBe(true); + expect(isRegExp(new RegExp('test'))).toBe(true); + }); + + it('false if the value is not a regexp', () => { + expect(isRegExp('')).toBe(false); + expect(isRegExp(123)).toBe(false); + expect(isRegExp(true)).toBe(false); + expect(isRegExp(null)).toBe(false); + expect(isRegExp(undefined)).toBe(false); + expect(isRegExp([])).toBe(false); + expect(isRegExp({})).toBe(false); + expect(isRegExp(new Map())).toBe(false); + expect(isRegExp(new Set())).toBe(false); + }); + }); + + describe('isDate', () => { + it('true if the value is a date', () => { + expect(isDate(new Date())).toBe(true); + }); + + it('false if the value is not a date', () => { + expect(isDate('')).toBe(false); + expect(isDate(123)).toBe(false); + expect(isDate(true)).toBe(false); + expect(isDate(null)).toBe(false); + expect(isDate(undefined)).toBe(false); + expect(isDate([])).toBe(false); + expect(isDate({})).toBe(false); + expect(isDate(new Map())).toBe(false); + expect(isDate(new Set())).toBe(false); + }); + }); + + describe('isError', () => { + it('true if the value is an error', () => { + expect(isError(new Error())).toBe(true); + }); + + it('false if the value is not an error', () => { + expect(isError('')).toBe(false); + expect(isError(123)).toBe(false); + expect(isError(true)).toBe(false); + expect(isError(null)).toBe(false); + expect(isError(undefined)).toBe(false); + expect(isError([])).toBe(false); + expect(isError({})).toBe(false); + expect(isError(new Map())).toBe(false); + expect(isError(new Set())).toBe(false); + }); + }); + + describe('isPromise', () => { + it('true if the value is a promise', () => { + expect(isPromise(new Promise(() => {}))).toBe(true); + }); + + it('false if the value is not a promise', () => { + expect(isPromise('')).toBe(false); + expect(isPromise(123)).toBe(false); + expect(isPromise(true)).toBe(false); + expect(isPromise(null)).toBe(false); + expect(isPromise(undefined)).toBe(false); + expect(isPromise([])).toBe(false); + expect(isPromise({})).toBe(false); + expect(isPromise(new Map())).toBe(false); + expect(isPromise(new Set())).toBe(false); + }); + }); + + describe('isMap', () => { + it('true if the value is a map', () => { + expect(isMap(new Map())).toBe(true); + }); + + it('false if the value is not a map', () => { + expect(isMap('')).toBe(false); + expect(isMap(123)).toBe(false); + expect(isMap(true)).toBe(false); + expect(isMap(null)).toBe(false); + expect(isMap(undefined)).toBe(false); + expect(isMap([])).toBe(false); + expect(isMap({})).toBe(false); + expect(isMap(new Set())).toBe(false); + }); + }); + + describe('isSet', () => { + it('true if the value is a set', () => { + expect(isSet(new Set())).toBe(true); + }); + + it('false if the value is not a set', () => { + expect(isSet('')).toBe(false); + expect(isSet(123)).toBe(false); + expect(isSet(true)).toBe(false); + expect(isSet(null)).toBe(false); + expect(isSet(undefined)).toBe(false); + expect(isSet([])).toBe(false); + expect(isSet({})).toBe(false); + expect(isSet(new Map())).toBe(false); + }); + }); + + describe('isWeakMap', () => { + it('true if the value is a weakmap', () => { + expect(isWeakMap(new WeakMap())).toBe(true); + }); + + it('false if the value is not a weakmap', () => { + expect(isWeakMap('')).toBe(false); + expect(isWeakMap(123)).toBe(false); + expect(isWeakMap(true)).toBe(false); + expect(isWeakMap(null)).toBe(false); + expect(isWeakMap(undefined)).toBe(false); + expect(isWeakMap([])).toBe(false); + expect(isWeakMap({})).toBe(false); + expect(isWeakMap(new Map())).toBe(false); + expect(isWeakMap(new Set())).toBe(false); + }); + }); + + describe('isWeakSet', () => { + it('true if the value is a weakset', () => { + expect(isWeakSet(new WeakSet())).toBe(true); + }); + + it('false if the value is not a weakset', () => { + expect(isWeakSet('')).toBe(false); + expect(isWeakSet(123)).toBe(false); + expect(isWeakSet(true)).toBe(false); + expect(isWeakSet(null)).toBe(false); + expect(isWeakSet(undefined)).toBe(false); + expect(isWeakSet([])).toBe(false); + expect(isWeakSet({})).toBe(false); + expect(isWeakSet(new Map())).toBe(false); + expect(isWeakSet(new Set())).toBe(false); + }); + }); +}); \ No newline at end of file diff --git a/packages/stdlib/src/types/js/complex.ts b/packages/stdlib/src/types/js/complex.ts new file mode 100644 index 0000000..94e4eb0 --- /dev/null +++ b/packages/stdlib/src/types/js/complex.ts @@ -0,0 +1,81 @@ +import { toString } from '.'; + +/** + * Check if a value is an array. + * + * @param {any} value + * @returns {value is any[]} + */ +export const isArray = (value: any): value is any[] => Array.isArray(value); + +/** + * Check if a value is an object. + * + * @param {any} value + * @returns {value is object} + */ +export const isObject = (value: any): value is object => toString(value) === '[object Object]'; + +/** + * Check if a value is a regexp. + * + * @param {any} value + * @returns {value is RegExp} + */ +export const isRegExp = (value: any): value is RegExp => toString(value) === '[object RegExp]'; + +/** + * Check if a value is a date. + * + * @param {any} value + * @returns {value is Date} + */ +export const isDate = (value: any): value is Date => toString(value) === '[object Date]'; + +/** + * Check if a value is an error. + * + * @param {any} value + * @returns {value is Error} + */ +export const isError = (value: any): value is Error => toString(value) === '[object Error]'; + +/** + * Check if a value is a promise. + * + * @param {any} value + * @returns {value is Promise} + */ +export const isPromise = (value: any): value is Promise => toString(value) === '[object Promise]'; + +/** + * Check if a value is a map. + * + * @param {any} value + * @returns {value is Map} + */ +export const isMap = (value: any): value is Map => toString(value) === '[object Map]'; + +/** + * Check if a value is a set. + * + * @param {any} value + * @returns {value is Set} + */ +export const isSet = (value: any): value is Set => toString(value) === '[object Set]'; + +/** + * Check if a value is a weakmap. + * + * @param {any} value + * @returns {value is WeakMap} + */ +export const isWeakMap = (value: any): value is WeakMap => toString(value) === '[object WeakMap]'; + +/** + * Check if a value is a weakset. + * + * @param {any} value + * @returns {value is WeakSet} + */ +export const isWeakSet = (value: any): value is WeakSet => toString(value) === '[object WeakSet]'; diff --git a/packages/stdlib/src/types/js/index.ts b/packages/stdlib/src/types/js/index.ts new file mode 100644 index 0000000..70d8433 --- /dev/null +++ b/packages/stdlib/src/types/js/index.ts @@ -0,0 +1,3 @@ +export * from './casts'; +export * from './primitives'; +export * from './complex'; \ No newline at end of file diff --git a/packages/stdlib/src/types/js/primitives.test.ts b/packages/stdlib/src/types/js/primitives.test.ts new file mode 100644 index 0000000..799aa59 --- /dev/null +++ b/packages/stdlib/src/types/js/primitives.test.ts @@ -0,0 +1,101 @@ +import { describe, expect, it } from 'vitest'; +import { isBoolean, isFunction, isNumber, isBigInt, isString, isSymbol, isUndefined, isNull } from './primitives'; + +describe('primitives', () => { + describe('isBoolean', () => { + it('true if the value is a boolean', () => { + expect(isBoolean(true)).toBe(true); + expect(isBoolean(false)).toBe(true); + }); + + it('false if the value is not a boolean', () => { + expect(isBoolean(0)).toBe(false); + expect(isBoolean('true')).toBe(false); + expect(isBoolean(null)).toBe(false); + }); + }); + + describe('isFunction', () => { + it('true if the value is a function', () => { + expect(isFunction(() => {})).toBe(true); + expect(isFunction(function() {})).toBe(true); + }); + + it('false if the value is not a function', () => { + expect(isFunction(123)).toBe(false); + expect(isFunction('function')).toBe(false); + expect(isFunction(null)).toBe(false); + }); + }); + + describe('isNumber', () => { + it('true if the value is a number', () => { + expect(isNumber(123)).toBe(true); + expect(isNumber(3.14)).toBe(true); + }); + + it('false if the value is not a number', () => { + expect(isNumber('123')).toBe(false); + expect(isNumber(null)).toBe(false); + }); + }); + + describe('isBigInt', () => { + it('true if the value is a bigint', () => { + expect(isBigInt(BigInt(123))).toBe(true); + }); + + it('false if the value is not a bigint', () => { + expect(isBigInt(123)).toBe(false); + expect(isBigInt('123')).toBe(false); + expect(isBigInt(null)).toBe(false); + }); + }); + + describe('isString', () => { + it('true if the value is a string', () => { + expect(isString('hello')).toBe(true); + }); + + it('false if the value is not a string', () => { + expect(isString(123)).toBe(false); + expect(isString(null)).toBe(false); + }); + }); + + describe('isSymbol', () => { + it('true if the value is a symbol', () => { + expect(isSymbol(Symbol())).toBe(true); + }); + + it('false if the value is not a symbol', () => { + expect(isSymbol(123)).toBe(false); + expect(isSymbol('symbol')).toBe(false); + expect(isSymbol(null)).toBe(false); + }); + }); + + describe('isUndefined', () => { + it('true if the value is undefined', () => { + expect(isUndefined(undefined)).toBe(true); + }); + + it('false if the value is not undefined', () => { + expect(isUndefined(null)).toBe(false); + expect(isUndefined(123)).toBe(false); + expect(isUndefined('undefined')).toBe(false); + }); + }); + + describe('isNull', () => { + it('true if the value is null', () => { + expect(isNull(null)).toBe(true); + }); + + it('false if the value is not null', () => { + expect(isNull(undefined)).toBe(false); + expect(isNull(123)).toBe(false); + expect(isNull('null')).toBe(false); + }); + }); +}); \ No newline at end of file diff --git a/packages/stdlib/src/types/js/primitives.ts b/packages/stdlib/src/types/js/primitives.ts new file mode 100644 index 0000000..25958f2 --- /dev/null +++ b/packages/stdlib/src/types/js/primitives.ts @@ -0,0 +1,65 @@ +import { toString } from '.'; + +/** + * Check if a value is a boolean. + * + * @param {any} value + * @returns {value is boolean} + */ +export const isBoolean = (value: any): value is boolean => typeof value === 'boolean'; + +/** + * Check if a value is a function. + * + * @param {any} value + * @returns {value is Function} + */ +export const isFunction = (value: any): value is T => typeof value === 'function'; + +/** + * Check if a value is a number. + * + * @param {any} value + * @returns {value is number} + */ +export const isNumber = (value: any): value is number => typeof value === 'number'; + +/** + * Check if a value is a bigint. + * + * @param {any} value + * @returns {value is bigint} + */ +export const isBigInt = (value: any): value is bigint => typeof value === 'bigint'; + +/** + * Check if a value is a string. + * + * @param {any} value + * @returns {value is string} + */ +export const isString = (value: any): value is string => typeof value === 'string'; + +/** + * Check if a value is a symbol. + * + * @param {any} value + * @returns {value is symbol} + */ +export const isSymbol = (value: any): value is symbol => typeof value === 'symbol'; + +/** + * Check if a value is a undefined. + * + * @param {any} value + * @returns {value is undefined} + */ +export const isUndefined = (value: any): value is undefined => toString(value) === '[object Undefined]'; + +/** + * Check if a value is a null. + * + * @param {any} value + * @returns {value is null} + */ +export const isNull = (value: any): value is null => toString(value) === '[object Null]'; diff --git a/packages/stdlib/src/types/ts/index.ts b/packages/stdlib/src/types/ts/index.ts new file mode 100644 index 0000000..961e79e --- /dev/null +++ b/packages/stdlib/src/types/ts/index.ts @@ -0,0 +1 @@ +export * from './string'; \ No newline at end of file diff --git a/packages/stdlib/src/types/ts/string.test-d.ts b/packages/stdlib/src/types/ts/string.test-d.ts new file mode 100644 index 0000000..88d016d --- /dev/null +++ b/packages/stdlib/src/types/ts/string.test-d.ts @@ -0,0 +1,43 @@ +import { describe, expectTypeOf, it } from 'vitest'; +import type { HasSpaces, Trim } from './string'; + +describe('string', () => { + describe('Trim', () => { + it('remove leading and trailing spaces from a string', () => { + type actual = Trim<' hello '>; + type expected = 'hello'; + + expectTypeOf().toEqualTypeOf(); + }); + + it('remove only leading spaces from a string', () => { + type actual = Trim<' hello'>; + type expected = 'hello'; + + expectTypeOf().toEqualTypeOf(); + }); + + it('remove only trailing spaces from a string', () => { + type actual = Trim<'hello '>; + type expected = 'hello'; + + expectTypeOf().toEqualTypeOf(); + }); + }); + + describe('HasSpaces', () => { + it('check if a string has spaces', () => { + type actual = HasSpaces<'hello world'>; + type expected = true; + + expectTypeOf().toEqualTypeOf(); + }); + + it('check if a string has no spaces', () => { + type actual = HasSpaces<'helloworld'>; + type expected = false; + + expectTypeOf().toEqualTypeOf(); + }); + }); +}); \ No newline at end of file diff --git a/packages/stdlib/src/types/ts/string.ts b/packages/stdlib/src/types/ts/string.ts new file mode 100644 index 0000000..0398e8e --- /dev/null +++ b/packages/stdlib/src/types/ts/string.ts @@ -0,0 +1,9 @@ +/** + * Trim leading and trailing whitespace from `S` + */ +export type Trim = S extends ` ${infer R}` ? Trim : S extends `${infer L} ` ? Trim : S; + +/** + * Check if `S` has any spaces + */ +export type HasSpaces = S extends `${string} ${string}` ? true : false;