Skip to main content

latest

Stage 3 Decorator factory to help you painlessly create aliases between different class members. Supports class methods, getters, setters, fields, and accessors.

This package works with Bun, Node.js, DenoIt is unknown whether this package works with Cloudflare Workers, Browsers
JSR Score
94%
Published
2 months ago (0.1.2)

alias

The @decorators/alias package provides a simple decorator factory named @alias, which allows you to alias one class member to another with zero configuration. It supports aliasing class fields, accessors, methods, getters, and setters. Compatible with both the instance side and the static side of any TypeScript/JavaScript class.

Getting Started

All @decorators/* packages are published exclusively to JSR.io, a new registry for TypeScript and JavaScript developers, with a keen focus on performance, security, and compatibility.

1. Install

Deno

deno add @decorators/alias

jsr add @decorators/alias

Additional Package Managers

Bun

bunx jsr add @decorators/alias

npm

npx jsr add @decorators/alias

pnpm

pnpx jsr add @decorators/alias

Yarn

yarn dlx jsr add @decorators/alias

Installation Requirements

This decorator is developed using the latest iteration of the TC39 Decorators Proposal; these are commonly referred to as "Stage 3" or "ES" Decorators, to distinguish them from the legacy TypeScript decorators that are enabled/disabled via the experimentalDecorators compiler flag.

Stage 3 decorators do not require any special configuration to use - they are enabled by default in TypeScript 5.0+ and Deno 1.40.0+. If you're using an older version of TypeScript, you should consider upgrading to the latest version to take advantage of this feature.

Deno recently rolled out support for Stage 3 Decorators in the 1.40.0 release. It's the first runtime to support this feature out of the box, and is my personal choice for TypeScript development. If you're not already using Deno, I highly recommend giving it a try - it's a fantastic runtime for beginners and experts alike.


2. Enjoy!

import { alias } from "@decorators/alias";

class Example {
  declare private hello: this["greet"];
  declare private welcome: this["greet"];

  private greeting = "Hello World";

  @alias.for("greeting")
  private message!: string;

  @alias("hello", "welcome")
  greet() {
    return this.message;
  }
}

Warning: Please ensure the experimentalDecorators option is disabled in your tsconfig.json or deno.json file's compilerOptions section.

This package will not work if legacy TypeScript decorators are enabled.


API

There are two different approaches to creating aliases with this package, both of which are available from the same named import, alias.

alias

The first is to use @alias directly on a class member you wish to alias, and provide it with the name(s) of the alias(es) it should create to that member. This allows you to create any number of aliases for a given class member, with each alias name passed as an argument to the decorator factory.

Usage

class A {
  // these are important!
  declare b: this["a"];
  declare c: this["a"];

  @alias("b", "c")
  a() {
    // ...
  }
}

Signature

export function alias<
  Aliases extends readonly PropertyKey[],
  This extends object = object,
  Value = unknown,
>(...aliases: Aliases): {
  (target: unknown, context: DecoratorContext): void;
};

This signature is in a simplified form for the sake of brevity. See the source code in mod.ts for the actual function signature as it is used in the implementation.

Parameters

Name Info
...aliases The names of the aliases to create for the target member.

Returns

This decorator factory returns a decorator function that can be applied to any class member type, including fields, accessors, methods, getters, and setters. It cannot be used on the class itself. The decorator it returns does not return a replacement member, but instead leverages initialization side-effects via the addInitializer API, creating the alias(es) at initialization time.

Example

Take note of the logical flow in this approach, demonstrated below, where the aliases are defined on the original member that is being decorated. You can think of it in natural language as "The 'greeting' field is also aliased as 'message'.", or "The 'greet' method is also aliased as 'hello' and 'welcome'.".

import { alias } from "@decorators/alias";

class Example {
  declare private message: string;
  declare private hello: this["greet"];
  declare private welcome: this["greet"];

  @alias("message")
  private greeting = "Hello World";

  @alias("hello", "welcome")
  greet() {
    return this.message;
  }
}

const example = new Example();

// The original method:
console.log(example.greet()); // "Hello World"

// The aliases to that method:
console.log(example.hello()); // "Hello World"
console.log(example.welcome()); // "Hello World"
console.log(example.hello === example.greet); // true

Note: notice the property declarations in the following example. These ensure the TypeScript compiler recognizes each alias as a valid member of the class. If they were elided, tsc would raise compile errors complaining about non-existent properties.


alias.for

The second approach to aliasing class members with this package is to use the @alias.for method. Similar to the @alias function, this is also a decorator factory, but differs in that applied to the alias itself, providing it with the name of the member it should point to. This accepts only one argument, which must be the name of another existing member of the class.

Usage

class Hi {
  message = "Hello World";

  greet() {
    return this.message;
  }

  @alias.for("greet") // source: greet, target: hello
  hello!: this["greet"]; // this is defined at initialization

  @alias.for("greet") // source: greet, target: welcome
  welcome(): string { // this is overridden at initialization
    return this.greet();
  }
}

Signature

alias.for = function<Source extends PropertyKey>(source: Source): {
  (target: unknown, context: DecoratorContext): void;
};

Parameters

Name Info
source The name of the class member to alias with the decorated alias.

Returns

This decorator factory returns a decorator that can be applied to any class member type, including fields, accessors, methods, getters, and setters. It cannot be used on the class itself. The decorator it returns does not return a replacement member (except when used on a field/accessor), but instead leverages initialization side-effects via the addInitializer API, creating the alias(es) at initialization time.

Example

import { alias } from "@decorators/alias";

class Example {
  private greeting = "Hello World";

  @alias.for("greeting")
  private message!: string;

  greet() {
    return this.message;
  }

  @alias.for("greet")
  hello!: this["greet"];

  @alias.for("greet")
  welcome(): string {
    return this.greet();
  }
}

const example = new Example();

// The original method:
console.log(example.greet()); // "Hello World"

// The aliases to that method:
console.log(example.hello()); // "Hello World"
console.log(example.welcome()); // "Hello World"
console.log(example.hello === example.greet); // true

Legacy Decorators Compatibility

This decorator is designed to be used with the latest iteration of the TC39 Decorators Proposal. There are, however, future plans to add a compatibility layer for the Legacy Decorators format. This will improve compatibility for many users whose projects still rely on the experimentalDecorators compiler flag, such as those using NestJS for its Parameter Decorators (which are not included in the Stage 3 proposal).


MIT © Nicholas Berlette. All rights reserved.
GitHub · Issues · Docs

Examples

Example 1

import { alias } from "@decorators/alias";

class Example {
  // declaring our two aliases ahead of time to make typescript happy
  declare foo: this["bar"];
  declare baz: this["bar"];

Example 2

import { alias } from "@decorators/alias";

class Example {
  greeting = "Hello, World!";

  // creates an alias for the `greeting` property

Example 3

class Example {
  // declaring our two aliases ahead of time to make typescript happy
  declare foo: this["bar"];
  declare baz: this["bar"];

Add Package

deno add @decorators/alias

Import symbol

import * as mod from "@decorators/alias";

Add Package

npx jsr add @decorators/alias

Import symbol

import * as mod from "@decorators/alias";

Add Package

yarn dlx jsr add @decorators/alias

Import symbol

import * as mod from "@decorators/alias";

Add Package

pnpm dlx jsr add @decorators/alias

Import symbol

import * as mod from "@decorators/alias";

Add Package

bunx jsr add @decorators/alias

Import symbol

import * as mod from "@decorators/alias";