{"_id":"@copper.teapot.ferret/typed-toolkit","_rev":"3-5b6b6e5bbee0283e90b015307169512c","name":"@copper.teapot.ferret/typed-toolkit","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@copper.teapot.ferret/typed-toolkit","version":"0.1.0","keywords":["typescript","utilities","types"],"author":{"name":"Theo Pelegrina"},"license":"MIT","_id":"@copper.teapot.ferret/typed-toolkit@0.1.0","maintainers":[{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"}],"homepage":"https://github.com/theit0/typed-toolkit#readme","bugs":{"url":"https://github.com/theit0/typed-toolkit/issues"},"dist":{"shasum":"417f997aaaa4d276133c0828e55207249f7bec30","tarball":"https://registry.npmjs.org/@copper.teapot.ferret/typed-toolkit/-/typed-toolkit-0.1.0.tgz","fileCount":8,"integrity":"sha512-Y7GpIFzTBlYrYJngnIJ6Yzd83MhximHdJcIBc39t2gLBjzT1mEQ7S3dyKU0kAcl/8wkZiGhOrq8BzY89UIkxew==","signatures":[{"sig":"MEUCIQCjC+FELAIODeThzU+v+DwUevzMbh2pS/luqVQ4aB+P0gIgN7PjZLKFWQbdVINbno5TmQYpSbmo3J8RCOgAyT7XlpU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@copper.teapot.ferret%2ftyped-toolkit@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":30296},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"bc789488f9f28f4897f747dc3d10c311befc9d5c","scripts":{"ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build","dev":"tsup --watch","lint":"eslint src tests","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"},"repository":{"url":"git+https://github.com/theit0/typed-toolkit.git","type":"git"},"_npmVersion":"10.9.8","description":"Utility functions with types that don't lie.","directories":{},"_nodeVersion":"22.22.3","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.0.0","typescript":"^5.6.0","expect-type":"^0.20.0","@vitest/coverage-v8":"^2.0.0","@typescript-eslint/parser":"^8.0.0","@typescript-eslint/eslint-plugin":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/typed-toolkit_0.1.0_1779559840065_0.8618726584205474","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@copper.teapot.ferret/typed-toolkit","version":"0.1.1","keywords":["typescript","utilities","types"],"author":{"name":"Theo Pelegrina"},"license":"MIT","_id":"@copper.teapot.ferret/typed-toolkit@0.1.1","maintainers":[{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"}],"homepage":"https://github.com/theit0/typed-toolkit#readme","bugs":{"url":"https://github.com/theit0/typed-toolkit/issues"},"dist":{"shasum":"d3b7543742f3ef3d9a7b86b3794c541f96af9852","tarball":"https://registry.npmjs.org/@copper.teapot.ferret/typed-toolkit/-/typed-toolkit-0.1.1.tgz","fileCount":8,"integrity":"sha512-SNrX7FkGAkmokVPkUpddjLsGZiLbb+8Q0mcOVc0zIiyyYjIdl1MFPvbhLxCJK1MaB9SZAC5R1VStt382ywXSkw==","signatures":[{"sig":"MEYCIQCiqrydVcT9dfzn65kJLFxZQ1ayKxsuhoq8/vsq5ZO1FQIhANK0Z763OxYKbHarUk/kT4ioPzWnEWyBhbXqsvltdmVH","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@copper.teapot.ferret%2ftyped-toolkit@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":30516},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"2207b9b5d1e0d259db34a7121c2d668e7b2ff355","scripts":{"ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build","dev":"tsup --watch","lint":"eslint src tests","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit","test:watch":"vitest","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"},"repository":{"url":"git+https://github.com/theit0/typed-toolkit.git","type":"git"},"_npmVersion":"10.9.8","description":"Utility functions with types that don't lie.","directories":{},"_nodeVersion":"22.22.3","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","eslint":"^9.0.0","vitest":"^2.0.0","@eslint/js":"^9.0.0","typescript":"^5.6.0","expect-type":"^0.20.0","@vitest/coverage-v8":"^2.0.0","@typescript-eslint/parser":"^8.0.0","@typescript-eslint/eslint-plugin":"^8.0.0"},"_npmOperationalInternal":{"tmp":"tmp/typed-toolkit_0.1.1_1779560128083_0.0441972609856045","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@copper.teapot.ferret/typed-toolkit","version":"0.1.2","description":"Utility functions with types that don't lie.","keywords":["typescript","utilities","types"],"license":"MIT","author":{"name":"Theo Pelegrina"},"repository":{"type":"git","url":"git+https://github.com/theit0/typed-toolkit.git"},"homepage":"https://github.com/theit0/typed-toolkit#readme","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","lint":"eslint src tests","ci":"npm run lint && npm run typecheck && npm run test:coverage && npm run build"},"devDependencies":{"@eslint/js":"^9.0.0","@typescript-eslint/eslint-plugin":"^8.0.0","@typescript-eslint/parser":"^8.0.0","@vitest/coverage-v8":"^2.0.0","eslint":"^9.0.0","expect-type":"^0.20.0","tsup":"^8.0.0","typescript":"^5.6.0","vitest":"^2.0.0"},"_id":"@copper.teapot.ferret/typed-toolkit@0.1.2","gitHead":"4afa44b858d8092a31fcbdb6017c1b5accd5dbe4","bugs":{"url":"https://github.com/theit0/typed-toolkit/issues"},"_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-ZEA82SICKAI/uNPgxMYnzUuk7l3rPkMBnklP0qTEdWtHsF2lea6KFXW04HI0acfyZTjy5D98cF7aU/s02xbmWg==","shasum":"88ca7ea478410dbdf6473cf56e7dc5ecaeb036be","tarball":"https://registry.npmjs.org/@copper.teapot.ferret/typed-toolkit/-/typed-toolkit-0.1.2.tgz","fileCount":8,"unpackedSize":32040,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@copper.teapot.ferret%2ftyped-toolkit@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICslPCY/fCC1BGUIZwmXJv1BwkXYCO347ecxRhDP2oCXAiB+7c1WZwIf2iT6MqUWAkkg8Hkq7A73TtEWQya+yYHDcg=="}]},"_npmUser":{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"},"directories":{},"maintainers":[{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/typed-toolkit_0.1.2_1779561823011_0.9327538832585078"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-23T18:10:39.959Z","modified":"2026-05-23T18:43:43.414Z","0.1.0":"2026-05-23T18:10:40.238Z","0.1.1":"2026-05-23T18:15:28.276Z","0.1.2":"2026-05-23T18:43:43.157Z"},"bugs":{"url":"https://github.com/theit0/typed-toolkit/issues"},"author":{"name":"Theo Pelegrina"},"license":"MIT","homepage":"https://github.com/theit0/typed-toolkit#readme","keywords":["typescript","utilities","types"],"repository":{"type":"git","url":"git+https://github.com/theit0/typed-toolkit.git"},"description":"Utility functions with types that don't lie.","maintainers":[{"name":"copper.teapot.ferret","email":"copper.teapot.ferret@gmail.com"}],"readme":"# typed-toolkit\n\n[![CI](https://github.com/theit0/typed-toolkit/actions/workflows/ci.yml/badge.svg)](https://github.com/theit0/typed-toolkit/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@copper.teapot.ferret/typed-toolkit)](https://www.npmjs.com/package/@copper.teapot.ferret/typed-toolkit)\n\n> Utility functions with types that don't lie.\n\nA TypeScript utility library where every function's return type is exactly what your IDE should infer — no `any`, no casts, no surprises.\n\n## Install\n\n```sh\nnpm install @copper.teapot.ferret/typed-toolkit\n```\n\n## What makes this different\n\nMost utility libs return `Record<string, unknown>` where they could return the exact subtype. This library uses generics, mapped types, and conditional types so the compiler knows the answer before you do.\n\n```ts\nimport { pick } from \"@copper.teapot.ferret/typed-toolkit\";\n\nconst user = { id: 1, name: \"Alice\", password: \"secret\" };\nconst safe = pick(user, [\"id\", \"name\"]);\n//    ^? { id: number; name: string }  ← not Record<string, unknown>\n```\n\n## API\n\n### Object\n\n#### `pick<T, K>(obj, keys)` → `Pick<T, K>`\n\n```ts\nimport { pick } from \"@copper.teapot.ferret/typed-toolkit\";\n\npick({ a: 1, b: \"x\", c: true }, [\"a\", \"b\"]);\n// { a: 1, b: \"x\" }  typed as { a: number; b: string }\n```\n\n**Runtime contract:** `pick` trusts the type system. If called from plain JavaScript with a key that doesn't exist on the object, it silently returns `undefined` for that key — same behavior as property access. TypeScript prevents this at compile time; at runtime, garbage in = garbage out.\n\n#### `omit<T, K>(obj, keys)` → `Omit<T, K>`\n\n```ts\nimport { omit } from \"@copper.teapot.ferret/typed-toolkit\";\n\nomit({ a: 1, b: 2, c: 3 }, [\"b\", \"c\"]);\n// { a: 1 }  typed as { a: number }\n```\n\n#### `mapValues<K, V, U>(obj, fn)` → `Record<K, U>`\n\n```ts\nimport { mapValues } from \"@copper.teapot.ferret/typed-toolkit\";\n\nmapValues({ a: 1, b: 2 }, (v) => v * 10);\n// { a: 10, b: 20 }  typed as Record<\"a\" | \"b\", number>\n```\n\n### Array\n\n#### `chunk<T>(arr, size)` → `T[][]`\n\n```ts\nimport { chunk } from \"@copper.teapot.ferret/typed-toolkit\";\n\nchunk([1, 2, 3, 4, 5], 2);\n// [[1, 2], [3, 4], [5]]\n```\n\n#### `unique<T>(arr)` → `T[]`\n\n```ts\nimport { unique } from \"@copper.teapot.ferret/typed-toolkit\";\n\nunique([1, 2, 2, 3, 1]);\n// [1, 2, 3]\n```\n\n#### `uniqueBy<T, K>(arr, fn)` → `T[]`\n\n```ts\nimport { uniqueBy } from \"@copper.teapot.ferret/typed-toolkit\";\n\nuniqueBy([{ id: 1, v: \"a\" }, { id: 1, v: \"b\" }, { id: 2, v: \"c\" }], (x) => x.id);\n// [{ id: 1, v: \"a\" }, { id: 2, v: \"c\" }]\n```\n\n#### `groupBy<T, K>(arr, fn)` → `Partial<Record<K, T[]>>`\n\n```ts\nimport { groupBy } from \"@copper.teapot.ferret/typed-toolkit\";\n\ngroupBy([\"one\", \"two\", \"three\"], (s) => s.length);\n// { 3: [\"one\", \"two\"], 5: [\"three\"] }\n```\n\n#### `partition<T, U>(arr, predicate)` → `[U[], Exclude<T, U>[]]`\n\nWith a type predicate, the two halves are narrowed automatically:\n\n```ts\nimport { partition } from \"@copper.teapot.ferret/typed-toolkit\";\n\ntype Animal = { kind: \"dog\" } | { kind: \"cat\" };\nconst isDog = (a: Animal): a is { kind: \"dog\" } => a.kind === \"dog\";\n\nconst [dogs, cats] = partition(animals, isDog);\n//     ^? { kind: \"dog\" }[]   ^? { kind: \"cat\" }[]\n```\n\n### Parsing (returns `Result<T, E>`, never throws)\n\n```ts\ntype Result<T, E> = { ok: true; value: T } | { ok: false; error: E };\n```\n\n#### `parseJSON<T>(str)` → `Result<T, string>`\n\n```ts\nimport { parseJSON } from \"@copper.teapot.ferret/typed-toolkit\";\n\nconst result = parseJSON<{ name: string }>('{\"name\":\"Alice\"}');\nif (result.ok) console.log(result.value.name); // \"Alice\"\n```\n\n#### `parseNumber(str)` → `Result<number, string>`\n\n```ts\nimport { parseNumber } from \"@copper.teapot.ferret/typed-toolkit\";\n\nparseNumber(\"3.14\"); // { ok: true, value: 3.14 }\nparseNumber(\"abc\");  // { ok: false, error: '\"abc\" is not a valid number' }\n```\n\n#### `parseDate(str)` → `Result<Date, string>`\n\n```ts\nimport { parseDate } from \"@copper.teapot.ferret/typed-toolkit\";\n\nparseDate(\"2024-01-15\"); // { ok: true, value: Date }\nparseDate(\"nope\");       // { ok: false, error: '\"nope\" is not a valid date' }\n```\n\n### Predicates\n\n#### `isNonNull<T>(value)` → `value is NonNullable<T>`\n\n```ts\nimport { isNonNull } from \"@copper.teapot.ferret/typed-toolkit\";\n\nconst nums = [1, null, 2, undefined, 3].filter(isNonNull);\n//    ^? number[]\n```\n\n#### `isDefined<T>(value)` → `value is T`\n\n```ts\nimport { isDefined } from \"@copper.teapot.ferret/typed-toolkit\";\n\nconst strs = [\"a\", undefined, \"b\"].filter(isDefined);\n//    ^? string[]\n```\n\n### String\n\n#### `capitalize<T extends string>(str)` → `Capitalize<T>`\n\n```ts\nimport { capitalize } from \"@copper.teapot.ferret/typed-toolkit\";\n\ncapitalize(\"hello\");\n// \"Hello\"  typed as Capitalize<\"hello\">\n```\n\n## Architecture decisions\n\n**ADR-001 — Type tests alongside runtime tests.** Every function has both Vitest runtime tests and `expect-type` type-level assertions. A function can pass all runtime tests and still have wrong types; `expect-type` catches that at compile time.\n\n**ADR-002 — `tsup` as bundler.** Ships ESM + CJS from a single config. Consumers on both module systems get correct types via the `exports` field.\n\n**ADR-003 — `Result<T, E>` instead of exceptions.** Fallible functions return a tagged union that forces the caller to handle the error branch. No silent swallows, no unchecked exceptions.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}