English | Português
A collection of small, tree-shakeable TypeScript utility functions. Shipped as ESM + CJS with type definitions included.
Some helpers are tailored to Brazil (CPF, CNPJ, CEP, BR phone masks, PT-BR formatting), while the rest are locale-agnostic.
npm install @julianobazzi/utilsimport { formatDate, contains, omitFields } from "@julianobazzi/utils";
formatDate("2024-01-02"); // "02/01/24"
contains("a", ["a", "b"]); // true
omitFields({ a: 1, b: 2 }, ["b"]); // { a: 1 }CommonJS:
const { onlyNumbers } = require("@julianobazzi/utils");
onlyNumbers("(11) 98765-4321"); // "11987654321"All functions are exported flat from the package root, grouped internally by purpose.
formatDate(value?, { simplified?, fallback? })—DD/MM/YY(orDD/MM/YYYY)formatDateTime(date?, { simplified?, showSeconds?, fallback? })—DD/MM/YY HH:mm(optional 4-digit year and/or:ss)formatMonth(value?, { fallback? })—MM/YYYYformatHour(value?, { simplified?, fallback? })—HH:mm(orHH:mm:ss)formatMinutesToDuration(minutes?, { fallback?, spaced? })— human-readable duration, e.g.1h e 30 minformatSecondsToDuration(seconds?, { fallback?, spaced? })— same, from seconds (≥60s rounded to minutes)formatDuration(minutes?, { fallback?, spaced? })— deprecated alias offormatMinutesToDurationformatCurrency(value?, divisor = 100, { fallback? })— BRL currency, e.g.R$ 19,90formatCompactNumber(value?, { decimals?, fallback? })— compact notation (EN), e.g.1.5M,100KformatPercentage(value?, round = false, { fallback? })— percentage, e.g.12,50%formatBoolean(value?, { casing? })—Sim/Não(PT-BR yes/no)formatPhone(phone?, { fallback? })— BR phone mask (10 or 11 digits)formatBytes(bytes?, round = false, { casing? })— human-readable size, e.g.1.50 KBformatSecondsToTime(value?, showSeconds = true)—HH:mm:ss(orHH:mm)formatTimeAgo(date?, { fallback?, casing? })— elapsed time in PT-BR, e.g.5 diasformatAddress(address, { fallback? })— builds a single-line addressformatCityAndState(city?, state?, { fallback?, separator?, casing? })—"City - UF"(empty when both missing)formatWeekDay(date?, { fallback?, casing?, dateFormat? })— date + abbreviated weekday, e.g.15/6 - Sáb(dateFormatdefaultD/M)getAge(birthDate?)— age in full years (today); missing/invalid/future →0formatAge(birthDate?, { fallback? })— age as PT-BR text, e.g."36 anos"/"1 ano"formatCPF(value?, { fallback? })—000.000.000-00formatCNPJ(value?, { fallback? })—00.000.000/0000-00(supports alphanumeric CNPJ)formatDocument(value?, { fallback? })— formats as CPF or CNPJ based on lengthformatPostalCode(value?, { fallback? })— BR postal code (CEP)00000-000formatPlate(value?, { fallback? })— BR license plate: legacy →ABC-1234, Mercosul keepsABC1D23formatPIS(value?, { fallback? })— PIS/PASEP000.00000.00-0formatLongDate(value?, { fallback?, casing? })— date in full PT-BR, e.g.1º de julho de 2026numberToWords(value?, { fallback? })— integer spelled in PT-BR up to the trillions, e.g.mil duzentos e trinta e quatrocurrencyToWords(value?, divisor = 100, { fallback? })— BRL amount spelled in PT-BR (mirrorsformatCurrency), e.g.dezenove reais e noventa centavosappendValue(base?, value?, { separator?, fallback?, casing? })— joins two texts (each trimmed), e.g."a; b"applyCasing(value, casing?)—lowercase/uppercase/titlecase(titlecase keeps the rest of each word, so"KB"survives)removeAccents(value?)— strips accents, e.g.João→JoaoonlyNumbers(value?)— removes everything that is not a digitonlyAlphanumeric(value?)— removes non-alphanumerics + uppercase ("12.abc"→"12ABC")formatWithPattern(value?, pattern?)— char-agnostic mask (#= next char), e.g.'12345678900'+'###.###.###-##'→123.456.789-00truncate(value?, length = 40)— trims text and appends...getLastCharacter(value?)— last character of a stringabbreviateName(name?, { casing? })—"John Smith"→"John S."(titlecase normalizes:"JOAO SILVA"→"Joao S.")joinByKey(values, key, dividerOrOptions?)— joins one property from each object; 3rd arg is a divider string or{ divider?, sort? }, wheresort(true | "asc" | "desc") orders bykeyfirstmaskSecret(value?, { visibleStart = 5, visibleEnd = 5, mask = '••••••' })— partially masks a secret, keeping the ends visible, e.g.$2y$1••••••lMnOp(short values → mask only)slugify(value?)— URL-safe slug (accent-free, lowercase, hyphenated), e.g."São Paulo"→sao-paulosanitizeSpreadsheetCell(value?)— guards CSV/Excel formula injection: prefixes'when the value starts with= + - @(tab/CR)buildWhatsAppUrl(phone?, message?, { countryCode = '55', fallback? })—wa.melink;countryCodeacceptsnullto omit, e.g.https://wa.me/5511987654321?text=...buildPhoneUrl(phone?, { countryCode = '55', fallback? })—tel:link, e.g.tel:+5511987654321(countryCode: null→tel:11987654321)buildEmailUrl(email?, { subject?, body?, fallback? })—mailto:link with optional encodedsubject/bodybuildInstagramUrl(username?, { fallback? })—https://instagram.com/<handle>(strips a leading@)buildFacebookUrl(username?, { fallback? })—https://facebook.com/<handle>(strips a leading@)buildLinkedInUrl(handle?, { type = 'profile', fallback? })—https://linkedin.com/in/...or/company/...(viatype)
Input mask patterns (react-input-mask convention: 9 = digit, a = letter, * = alphanumeric).
CPF_MASK—999.999.999-99CNPJ_MASK—99.999.999/9999-99CNPJ_ALPHANUMERIC_MASK—**.***.***/****-99PHONE_MASK—(99) 9999-9999(landline)CELLPHONE_MASK—(99) 99999-9999(mobile)POSTAL_CODE_MASK—99999-999(CEP)PLATE_MASK—aaa-9*99(license plate; the*slot covers legacy and Mercosul)PIS_MASK—999.99999.99-9(PIS/PASEP)
contains(value, items)—trueifvalueis initemsisOdd(value)—truefor odd numbers (handles negatives)isValidJson(value?)—trueif the string is valid JSONisValidBarcode(value)—truefor a valid EAN/GTIN check digitisValidUrl(value)—truefor a validhttp/httpsURLisDateString(value?)—truefor an ISO dateYYYY-MM-DD(no time)isDateTimeString(value?)—truefor a date-time (Tor space separator,HH:mm[:ss])isValidPhone(value?)—truefor a valid BR phone (landline or mobile)isBirthday(value?)—trueif the date falls on today's day/monthisValidCPF(value?)—truefor a CPF with valid check digitsisValidCNPJ(value?)—truefor a valid CNPJ (numeric or alphanumeric)isValidDocument(value?)— validates as CPF or CNPJ based on lengthisValidPostalCode(value?)—truefor an 8-digit CEPisValidEmail(value?)—truefor a valid emailisValidUF(value?)—truefor a valid BR state abbreviation (case-insensitive)isValidPlate(value?)—truefor a BR license plate (legacyAAA9999or MercosulAAA9A99)isValidPIS(value?)—truefor a PIS/PASEP with a valid check digitisValidRenavam(value?)—truefor a valid RENAVAM (11 digits or legacy 9–10)isValidCNH(value?)—truefor a CNH with valid check digits (Denatran algorithm)isValidVoterId(value?)—truefor a valid voter registration number (título de eleitor)isValidBoleto(value?)—truefor a valid boleto digitable line (bank slip or collection)
precisionRound(value?, precision = 2)— rounds to N decimal placesformatInteger(value?, { fallback? })— rounds to the nearest integertoPositive(value?)— clamps to a non-negative valuegetRandomInt(min = 1, max = 100)— random integer in range (inclusive)safeDivide(value1, value2?)— divides; returns 0 when the divisor is ≤ 0 or missingtoCents(value?)— amount → integer cents, e.g.19.9→1990(inverse offormatCurrency)parseCurrencyToCents(value?)— BRL string → integer cents, e.g."R$ 1.234,56"→123456
getProperty(obj, key)— type-safe property accessomitFields(obj, keys)— shallow copy withoutkeysgetOptionId(option?)— extracts theidfrom an option/entitygetListIds(list?)— maps a list of entities to theiridsfindOptionById(options?, value?)— finds the option whose id matchesvalue(string compare), ornullfindOptionsByIds(options?, value?)— maps each id invalueto its option, dropping non-matchesgetLabelById(options?, value?, key = "name", fallback = "")— option's field as a string by id, orfallback
parseIds(...ids)— comma-separated id strings →number[]resolveIdsToObjects(ids?, resolver, params?)— resolves an id list into objects via an async resolver (in parallel)resolveList(value?, resolver, params?)—parseIds+resolveIdsToObjects; accepts a string or string arrayresolveId(value?, resolver, params?)— resolves the first valid id into an object, ornull
Dependency-free transforms with the (value, originalValue) => string shape (matches yup.transform); they wrap the base helpers.
onlyNumbersTransform(_value, originalValue)— wrapsonlyNumbers, e.g.yup.string().transform(onlyNumbersTransform)onlyAlphanumericTransform(_value, originalValue)— wrapsonlyAlphanumeric, e.g.yup.string().transform(onlyAlphanumericTransform)
Every generator produces random values that pass the matching validator.
generateCPF({ formatted? })— valid CPF;formatted: true→000.000.000-00generateCNPJ({ formatted?, alphanumeric? })— valid CNPJ (branch0001);alphanumeric: true→ 2026 formatgeneratePIS({ formatted? })— valid PIS/PASEP;formatted: true→000.00000.00-0generateRenavam({ legacy? })— valid RENAVAM;legacy: true→ old 9-digit formatgenerateCNH()— valid CNH (Denatran algorithm)generateVoterId()— valid voter registration number (random state code 01–28)generatePlate({ mercosul?, formatted? })— valid plate; Mercosul by default,mercosul: false→ legacygenerateBarcode({ length? })— valid EAN/GTIN barcode (8/12/13/14 digits, default EAN-13)generatePhone({ mobile?, formatted? })— valid BR phone; mobile by default,mobile: false→ landlinegeneratePostalCode({ formatted? })— 8-digit CEP;formatted: true→00000-000
loadImageFromBlob(blob)—Promise<HTMLImageElement>getImageDimensions(file)—Promise<{ width, height, extension }>isPhotoLandscape(fileOrUrl)—Promise<boolean>(width > height)isNotificationsSupported()— checks web push support
| Script | Description |
|---|---|
npm run build |
Bundles into dist/ (ESM + CJS + .d.ts) via tsup |
npm run dev |
Build in watch mode |
npm run test |
Runs the tests once (Vitest) |
npm run test:watch |
Runs the tests in watch mode |
npm run test:coverage |
Runs the tests with coverage (thresholds enforced) |
npm run typecheck |
Type-checks with tsc --noEmit |
npm run lint |
Lint + format check (Biome) |
npm run lint:fix |
Applies safe lint/format fixes |
- Create
src/<group>/<name>.tswith a named export (export function <name>). - Add
src/<group>/<name>.test.tswith Vitest tests. - Re-export it from the group barrel
src/<group>/index.ts. - New group? Create
src/<group>/index.tsand include it insrc/index.ts.
MIT © Juliano Bazzi