---
title: "Introduction"
description: "Install @vinicunca/perkakas, a typed utility library with data-first and data-last APIs, pipes, and lazy evaluation."
canonical_url: "https://vinicunca.dev/perkakas/getting-started"
---
# Introduction

> Install @vinicunca/perkakas, a typed utility library with data-first and data-last APIs, pipes, and lazy evaluation.

`@vinicunca/perkakas` is a TypeScript utility library. Every helper is written in TypeScript, so types are part of the implementation rather than a later add-on. You can call the same function **data-first** (data as the first argument) or **data-last** (to drop into a [`pipe`](https://vinicunca.dev/perkakas/functions#pipe)).

It is a fork of [Remeda](https://remeda.org). The runtime API is the Remeda surface plus a few Perkakas-only helpers. The one rename to know: Remeda's `purry` is [`curry`](https://vinicunca.dev/perkakas/functions#curry) here.

## Installation

```bash [pnpm]
pnpm add @vinicunca/perkakas
```

```bash [npm]
npm install @vinicunca/perkakas
```

```bash [yarn]
yarn add @vinicunca/perkakas
```

```bash [bun]
bun add @vinicunca/perkakas
```

Import the functions you need. Tree-shaking drops the rest.

```ts
import { filter, map, pipe } from '@vinicunca/perkakas';
```

## Data-first and data-last

Libraries like Lodash put data first (`_.pick(obj, keys)`). Ramda puts data last (`R.pick(keys, obj)`). Perkakas ships **both** on the same import. Data-first is the usual call. Data-last omits the data argument and returns a function — that is what you pass to `pipe`.

```ts
import { pick, pipe } from '@vinicunca/perkakas';

pick(obj, ['firstName', 'lastName']); // data-first
pipe(obj, pick(['firstName', 'lastName'])); // data-last
```

Data-first is kinder to autocomplete: your editor already knows the object's keys when you type the second argument.

```ts
import { filter, groupBy, pipe } from '@vinicunca/perkakas';

const DATA = [
  { name: 'john', age: 20, gender: 'm' },
  { name: 'marry', age: 22, gender: 'f' },
  { name: 'samara', age: 24, gender: 'f' },
  { name: 'bill', age: 33, gender: 'm' }
];

pipe(
  DATA,
  filter((x) => x.gender === 'f'),
  groupBy((x) => x.age)
);
```

## Pipe

[`pipe`](https://vinicunca.dev/perkakas/functions#pipe) runs functions left to right. You read the transformation in the order it runs, without naming every intermediate value.

```ts
import { filter, map, pipe } from '@vinicunca/perkakas';

pipe(
  [1, 2, 3, 4],
  filter((n) => n % 2 === 0),
  map((n) => n * 2)
); // => [4, 8]
```

Any function that takes one argument works in a pipe, not only Perkakas helpers. Wrap other calls in an arrow if they are not data-last: `($) => validate($, CONFIG)`.

A headless variant, [`piped`](https://vinicunca.dev/perkakas/functions#piped), builds a reusable pipeline without the initial data.

## Lazy evaluation

When consecutive **lazy** helpers sit in a `pipe` in their data-last form (`map`, `filter`, `take`, `unique`, and others tagged Lazy on the [Functions](https://vinicunca.dev/perkakas/functions) page), Perkakas walks items one at a time. Later steps can stop early so the rest of the input is never visited.

```ts
import { map, pipe, take, unique } from '@vinicunca/perkakas';

pipe(
  [1, 2, 2, 3, 3, 4, 5, 6],
  map((x) => {
    console.log('iterate', x);
    return x;
  }),
  unique(),
  take(3)
); // => [1, 2, 3]

// Console:
// iterate 1
// iterate 2
// iterate 2
// iterate 3
```

To force eager evaluation of a lazy helper, call it data-first inside an arrow: `($) => map($, callback)` instead of `map(callback)`.

## Type guards

Functions in the Guard category are type predicates. After `isString(value)` (or `filter(isString)`), TypeScript narrows the value to `string`.

```ts
import { filter, isString } from '@vinicunca/perkakas';

isString('ok'); // => true
isString(1); // => false

filter(['a', 1, 'b'], isString); // => ['a', 'b']
```

- [`hasProp`](https://vinicunca.dev/perkakas/functions#hasProp) — Checks if `data` has a prop `key`.
- [`hasSubObject`](https://vinicunca.dev/perkakas/functions#hasSubObject) — Checks if `subObject` is a sub-object of `object`, which means for every
- [`isArray`](https://vinicunca.dev/perkakas/functions#isArray) — A function that checks if the passed parameter is an Array and narrows its type accordingly.
- [`isBigInt`](https://vinicunca.dev/perkakas/functions#isBigInt) — A function that checks if the passed parameter is a bigint and narrows its
- [`isBoolean`](https://vinicunca.dev/perkakas/functions#isBoolean) — A function that checks if the passed parameter is a boolean and narrows its type accordingly.
- [`isDate`](https://vinicunca.dev/perkakas/functions#isDate) — A function that checks if the passed parameter is a Date and narrows its type accordingly.
- [`isDeepEqual`](https://vinicunca.dev/perkakas/functions#isDeepEqual) — Performs a *deep structural* comparison between two values to determine if
- [`isDefined`](https://vinicunca.dev/perkakas/functions#isDefined) — A function that checks if the passed parameter is defined (`!== undefined`)
- [`isEmpty`](https://vinicunca.dev/perkakas/functions#isEmpty) — A function that checks if the passed parameter is empty.
- [`isEmptyish`](https://vinicunca.dev/perkakas/functions#isEmptyish) — A function that checks if the input is empty. Empty is defined as anything
- [`isError`](https://vinicunca.dev/perkakas/functions#isError) — A function that checks if the passed parameter is an Error and narrows its type accordingly.
- [`isFunction`](https://vinicunca.dev/perkakas/functions#isFunction) — A function that checks if the passed parameter is a Function and narrows its type accordingly.
- [`isIncludedIn`](https://vinicunca.dev/perkakas/functions#isIncludedIn) — Checks if the item is included in the container. This is a wrapper around
- [`isNonNull`](https://vinicunca.dev/perkakas/functions#isNonNull) — A function that checks if the passed parameter is not `null` and narrows its type accordingly.
- [`isNonNullish`](https://vinicunca.dev/perkakas/functions#isNonNullish) — A function that checks if the passed parameter is defined *AND* isn't `null`
- [`isNot`](https://vinicunca.dev/perkakas/functions#isNot) — A function that takes a guard function as predicate and returns a guard that negates it.
- [`isNullish`](https://vinicunca.dev/perkakas/functions#isNullish) — A function that checks if the passed parameter is either `null` or
- [`isNumber`](https://vinicunca.dev/perkakas/functions#isNumber) — A function that checks if the passed parameter is a number and narrows its
- [`isObjectType`](https://vinicunca.dev/perkakas/functions#isObjectType) — Checks if the given parameter is of type `"object"` via `typeof`, excluding `null`.
- [`isPlainObject`](https://vinicunca.dev/perkakas/functions#isPlainObject) — Checks if `data` is a "plain" object. A plain object is defined as an object with string keys and values of any type, including primitives, other objects, functions, classes, etc (aka struct/shape/record/simple). Technically, a plain object is one whose prototype is either `Object.prototype` or `null`, ensuring it does not inherit properties or methods from other object types.
- [`isPromise`](https://vinicunca.dev/perkakas/functions#isPromise) — A function that checks if the passed parameter is a Promise and narrows its type accordingly.
- [`isShallowEqual`](https://vinicunca.dev/perkakas/functions#isShallowEqual) — Performs a *shallow structural* comparison between two values to determine if
- [`isStrictEqual`](https://vinicunca.dev/perkakas/functions#isStrictEqual) — Determines whether two values are *functionally identical* in all contexts.
- [`isString`](https://vinicunca.dev/perkakas/functions#isString) — A function that checks if the passed parameter is a string and narrows its type accordingly.
- [`isSymbol`](https://vinicunca.dev/perkakas/functions#isSymbol) — A function that checks if the passed parameter is a symbol and narrows its type accordingly.
- [`isTruthy`](https://vinicunca.dev/perkakas/functions#isTruthy) — A function that checks if the passed parameter is truthy and narrows its type accordingly.

## Types that stay strict

Helpers are written so mapped tuples stay tuples, and filters keep the narrowed type. You often do not need extra libraries just to get the type you already expected.

```ts
import { map } from '@vinicunca/perkakas';

const DATA = [1, 2, 3] as const;

DATA.map((x) => x.toString());
// => string[]

map(DATA, (x) => x.toString());
// => [string, string, string]
```

## Next

- Browse every export on the [Functions](https://vinicunca.dev/perkakas/functions) page (search and jump by category).
- Switching from Remeda, Lodash, Ramda, or Just? Start at [Migrate](https://vinicunca.dev/perkakas/migrate).

---

- [GitHub](https://github.com/vinicunca/perkakas)
- [npm](https://www.npmjs.com/package/@vinicunca/perkakas)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
