# @qiwi/decorator-utils

> Universal decorator factories made from scratch

Latest version **4.5.10** (published 2024-01-28) · MIT license · 0 weekly downloads

## Install

```sh
npm install @qiwi/decorator-utils
pnpm add @qiwi/decorator-utils
yarn add @qiwi/decorator-utils
bun add @qiwi/decorator-utils
```

## Health

**Score 40/100 (D)** — status: abandoned.

Positive: has types; esm support; no vulnerabilities; has provenance; high maintenance score; high quality score.

Warnings: low downloads.

Negative: abandoned; declining downloads.

## Facts

| | |
|---|---|
| Version | 4.5.10 |
| Published | 2024-01-28 |
| First published | 2019-02-03 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 61.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 9 |
| Author | Anton Golub |
| Maintainers | antongolub, qiwibot |
| Keywords | decorator, @decorator, annotation |

## Links

- npm: https://www.npmjs.com/package/@qiwi/decorator-utils
- Repository: https://github.com/qiwi/decorator-utils
- Homepage: https://github.com/qiwi/decorator-utils#readme
- Issues: https://github.com/qiwi/decorator-utils/issues
- npm.io page: https://npm.io/package/@qiwi/decorator-utils

## Dependencies (1)

- [lodash](https://npm.io/package/lodash.md) ^4.17.21

## Alternatives

- [localforage](https://npm.io/package/localforage.md) — 6.2M weekly downloads
- [localforage-observable](https://npm.io/package/localforage-observable.md) — 30.8K weekly downloads
- [@y/y](https://npm.io/package/@y/y.md) — 30.1K weekly downloads
- [@metaobjectsdev/render](https://npm.io/package/@metaobjectsdev/render.md) — 3.5K weekly downloads
- [@ledgerhq/coin-algorand](https://npm.io/package/@ledgerhq/coin-algorand.md) — 1.1K weekly downloads

## Recent versions

- 4.5.10 (latest) — 2024-01-28
- 4.5.4 — 2023-09-07
- 4.5.3 — 2023-05-23
- 4.5.2 — 2023-03-17
- 4.5.1 — 2023-03-17
- 4.5.0 — 2023-03-15
- 4.4.0 — 2023-03-14
- 4.3.0 — 2023-03-10
- 4.2.0 — 2023-03-10
- 4.1.0 — 2023-03-07
- 4.0.0 — 2023-03-07
- 4.0.0-beta.1 — 2023-03-07
- 4.0.0-beta.0 — 2023-03-07
- 3.3.0 — 2022-12-17
- 3.2.8 — 2022-02-12
- … 24 more at https://npm.io/package/@qiwi/decorator-utils/versions

## README

# decorator-utils
> Universal decorator factories made from scratch

[![Maintainability](https://api.codeclimate.com/v1/badges/4c341fd87383813f8e18/maintainability)](https://codeclimate.com/github/qiwi/decorator-utils/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/4c341fd87383813f8e18/test_coverage)](https://codeclimate.com/github/qiwi/decorator-utils/test_coverage)

## Install
```bash
yarn add @qiwi/decorator-utils
```

## Notes
* There's no right way to support both decorator types: with `@parentheses()` and `@plain`.
Holy War thread: [wycats/javascript-decorators/issues/23](https://github.com/wycats/javascript-decorators/issues/23)
* TypeScript 5 decorators: [announcing-typescript-5-0](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#decorators), [TS/issues/52435](https://github.com/microsoft/TypeScript/issues/52435), [nestjs/issues/10959](https://github.com/nestjs/nest/issues/10959)
* TC39 stage-3 [proposal decorators](https://github.com/tc39/proposal-decorators)

## Usage
#### Method
```ts
import {constructDecorator} from '@qiwi/decorator-utils'

const decorator = constructDecorator((targetType, target, param) => {
  if (targetType === METHOD) {
    return value => param || 'qux'
  }
})

class Foo {
  @decorator()
  foo () { return 'bar' }
  @decorator('BAZ')
  baz () { return 'baz' }
}
```

#### Class
```ts
const decorator = constructDecorator((targetType, target) => {
  if (targetType === CLASS) {
    return class Bar extends target {
      constructor (name, age) {
        super(name)
        this.age = age
      }
    }
  }
})

@decorator()
class Foo {
  constructor (name) {
    this.name = name
  }
  foo () { return 'bar' }
}
```

#### Field & Param
```ts
import {createDecorator, FIELD, PARAM} from '@qiwi/decorator-utils'

const meta: any = {}
const decorator = constructDecorator(({
  propName,
  paramIndex,
  targetType,
  target,
  args: [param]
}: IDecoratorHandlerContext) => {
  if (targetType === PARAM) {
    if (propName && typeof paramIndex === 'number') {
      meta[propName] = meta[propName] || {}
      meta[propName][paramIndex] = target
    }
  }

  if (targetType === FIELD) {
    if (propName) {
      meta[propName] = param
    }
  }
})

class Foo {
  @decorator('arg')
  foo = 'bar'

  bar(one: any, @decorator() two: any) {
    return 'bar'
  }
}

/**
    Now `meta` is smth like:
    {
      foo: 'arg',
      bar: {
        1: Foo.prototype.bar,
      },
    }
*/
```

You may also apply the decorator to the class, but decorate its methods:

```ts
const decorator = constructDecorator((targetType, target) => {
  if (targetType === METHOD) {
    return () => {
      return target().toUpperCase()
    }
  }
})

@decorator()
class Foo {
  foo () { return 'bar' }
  baz () { return 'baz' }
}
```

#### Context
`constructDecorator` factory provides the handler access to the decorator context.
This data describes the specifics of the decorated target, decorator arguments and so on.

```ts
type IDecoratorHandlerContext = {
  args: IDecoratorArgs
  kind: ITargetType | null        // targetType alias
  targetType: ITargetType | null
  target: ITarget
  proto: IProto
  ctor: Function
  name?: IPropName                // propName alias
  propName?: IPropName
  paramIndex?: IParamIndex
  descriptor?: IDescriptor
}
```

#### Options
You may set additional options to apply some decorator asserts: allowed target types, repeatable or not.

```ts
const plus = constructDecorator(
  ({ targetType, target, args: [param] }) => {
    return (value: number) => target(value) + param
  },
  {
    allowedTypes: METHOD, // string | string[]
    repeatable: true,
  },
)

class Foo {
  @plus(2)
  @plus(1)
  bar(v: number) {
    return v
  }
}
```

#### reflect-metadata
If the global `Reflect` provides `getMetadataKeys`, `getMetadata` and `defineMetadata` methods, they will be used to attach corresponding data to the decorated targets.
To enable, just add `reflect-metadata` to your dependencies and load it.
```ts
import 'reflect-metadata'
import {constructDecorator} from '@qiwi/decorator-utils'
// ...
```

### Refs
* [JS decorators by Axel Rauschmayer](https://2ality.com/2022/10/javascript-decorators.html)
* ["aspect" syntax for JS](https://github.com/tc39/proposal-decorators)
* [Exploring es7 decorators by Addy Osmany](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841)
* [core-decorators](https://www.npmjs.com/package/core-decorators)
* [lodash-decorators](https://www.npmjs.com/package/lodash-decorators)
* [decorator-utils](https://www.npmjs.com/package/decorator-utils)
* [Netanel Basal's decorator tips](https://netbasal.com/create-and-test-decorators-in-javascript-85e8d5cf879c)

## License
[MIT](./LICENSE)

---
_Source: https://npm.io/package/@qiwi/decorator-utils · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
