Tips for designing a custom TypeScript 5 property decorator

I have a decorator in TypeScript:

const bindMethod = (method: any): PropertyDecorator =>
    ((target: any, name?: PropertyKey): any => {
      if(name === undefined) {
        throw new Error('Bound decorator must be used with a property name.');
      }

      return {
        get(): any {
          return target[method].bind(this);
        }
      };
    });

class MyClass {

  @bindMethod('example')
  myMethod!: any;


  example(): string {
    return 'example';
  }

}

I am trying to convert this decorator to TypeScript 5. Here is my revised version:

const bindMethod = (<
    This,
    Value
>(method: keyof This): any => ((
    _value: undefined,
    context: ClassFieldDecoratorContext<This, Value>
) => {
  if(context.private) {
    throw new Error('Bound decorator cannot be used on private properties.');
  }

  context.addInitializer(function(this: This) {
    (this as any)[method] = (this[method] as CallableFunction).bind(this);
  });
}));

Here is the JavaScript output for the decorator:

const example = (i) => (n, r) => {
  if (r.private)
    throw new Error("Bound decorator cannot be used on private properties.");
  r.addInitializer(function() {
    this[i] = this[i].bind(this);
  });
};
export {
  example as bind
};

However, I am encountering a runtime error:

TypeError: context.addInitializer is not a function
.

What could be the issue here?

Here is the configuration I am using:

{
    "compilerOptions": {
        "allowUnreachableCode": false,
        "allowUnusedLabels": false,
        "alwaysStrict": true,
        "exactOptionalPropertyTypes": true,
        "noFallthroughCasesInSwitch": true,
        "noImplicitAny": true,
        "noImplicitOverride": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "noPropertyAccessFromIndexSignature": true,
        "noUncheckedIndexedAccess": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "strict": true,
        "strictBindCallApply": true,
        "strictFunctionTypes": true,
        "strictNullChecks": true,
        "strictPropertyInitialization": true,
        "useUnknownInCatchVariables": true,
        "module": "es6",
        "moduleResolution": "node10",
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "lib": [
            "es2017",
            "dom"
        ],
        "target": "es2017",
        "skipLibCheck": true,
        "composite": true,
        "rootDir": "./src",
        "outDir": "./dist"
    },
    "files": [
        "./src/bound.ts",
        "./src/index.ts"
    ],
    "include": [
        "./src/**/*.ts"
    ],
    "exclude": [
        "./node_modules",
        "./dist"
    ]
}

Answer №1

Learn how to incorporate ES decorators in your code using TypeScript version 5 or higher.
https://tsplay.dev/wO2azW

function createFieldBinder<B extends string>(method: B) {
    return function <T extends Record<B, T[K] & Function>, K extends keyof T>(
        value: undefined,
        context: ClassFieldDecoratorContext<T, T[K]> & {
            name: K;
            private: false;
            static: false;
        }
    ): (this: T, value: T[K]) => T[K] {
        return function fieldInitializer(this: T, value: T[K]): T[K] {
            return this[method].bind(this)
        }
    }
}

class Example {
    @createFieldBinder('exampleMethod')
    field!: this['exampleMethod']

    exampleMethod(): string {
        return 'exampleMethod';
    }
}

console.log(new Example().field())

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The property 'owlDateTimeTrigger' cannot be bound to 'span' as it is not recognized

I have integrated the OwlDateTimeModule into my smart-table-datepicker component. Although I imported it in my smart-table-datepicker.module file, I am still encountering errors. What could be causing this issue? smart-table-datepicker.module.ts import { ...

pressing the button again will yield a new outcome

I am looking to disable a button (material ui) when it is clicked for the second time by setting disabled={true}. Unfortunately, I have not been able to find any examples related to this specific scenario on StackOverflow. <Button onClick={this.s ...

The term "Movie" is not compatible as a JSX component

Currently working on a movie app project but encountering issues with handling arguments and displaying them properly using TypeScript. The challenge lies in trying to map the movie object, display them individually on the homepage, and showcase all the re ...

Use the IONIC CAPACITOR application to send a 20-byte command to a BLE device

Hello everyone, I am currently working on an application that is designed to connect to a BLE device. I have a requirement to write 20 Bytes to the device using BleClient.write function. 34h 01h 50h 4Fh 4Ch 49h 54h 45h 43h 00 00 00h 00h 00h 00h 00h 00h 00h ...

Unable to access property 'create' which is undefined

On my Ionic3 page, I am trying to trigger a modal open from within a click event function. export class HomePage { .... .... .... loadPos() { var randomLocations = Microsoft.Maps.TestDataGenerator.getLocations(5, this.map.getBounds()); ...

Centralized MUI design for varying screen dimensions

I am struggling to perfectly center my modal box in the middle of the screen. The problem arises when the screen size changes, causing the box to be misaligned. I attempted using top:50% and left: 50%, but this did not effectively center the box. center ...

Form submission returns JSON data with an undefined value from the server

I've been following a tutorial here but ran into some issues due to using newer versions of Angular and Ionic. This is an excerpt from my code: createReview(review){ let headers = new HttpHeaders(); headers.append('Content-Type&apo ...

How can we fetch data from the server in Vue 2.0 before the component is actually mounted?

Can anyone help me with this question noted in the title? How can I prevent a component from mounting in <router-view> until it receives data from the server, or how can I fetch the data before the component is mounted in <router-view>? Here a ...

Error encountered while attempting to load SWC binary for win32/ia32 in a Next JS application

Upon installing a Next.js app using the command npx create-next-app@latest, I encountered an error while running the app. Can anyone explain why this error occurred and provide a solution? PS D:\New folder\my-app> npm run dev [email pr ...

When building websites, pages, or applications with React, how do you determine the best choice between JavaScript, TypeScript, or JavaScriptXML?

After completing my portfolio and an eCommerce website design using Figma, I started learning React and Tailwind with Vite. I'm comfortable with basic JavaScript but now I want to understand the differences between .js, .jsx, and .ts files when workin ...

Creating a unique type with a suffix of `px`

Understanding how to create a Position type class is clear: class Position { x: number = 0; y: number = 0; } However, I now require the x and y values to be integers with the suffix of px, like this: const position = { x: '1px', y: &ap ...

When trying to use TypeScript with next.js, encountering an unexpected token `?` error is not

Having an issue with next.js, the command npm run dev keeps failing due to a syntax error related to an optional property in a tsx file: Syntax error: Unexpected token 44 | 45 | type State<T_HT> = { > 46 | ghostHighlight: ?{ | ...

Reactive form allows you to easily format dates

Currently, the date displayed is 1/4/2022. We need it to display in the format 01/04/2022. Can we achieve this formatting using reactive forms with the sample Model form provided below? Thank you. How can we format it when starting from transactionStartD ...

Using Selenium WebDriver with JavaScript: Handling Mouse Events (mouseover, click, keyup) in Selenium WebDriver

I am currently working on integrating Selenium testing for mouse events with dynamically generated elements. Specifically, I am attempting to trigger a "mouseover" event on an element and then interact with some icons within it. However, I have encountere ...

Create duplicates of both the array and its individual elements by value

I'm having trouble figuring out how to properly copy an array along with all its elements. In my model, I have a name and options, both of which are strings. This is what I currently have: const myArrayToDuplicate = [myModel, myModel, myModel] My ...

Encountered an error: Object(...) function not defined when using React, Formik, and Webpack

I have encountered an issue while trying to use both Formik and React-Form-Hooks in my project. Despite using Typescript as my language and Babel as the transpiler, both libraries throw the same error when compiled. Uncaught TypeError: Object(...) is not ...

What is the best way to retrieve the response from an Observable/http/async call in Angular?

My service returns an observable that makes an http request to my server and receives data. However, I am consistently getting undefined when trying to use this data. What could be causing this issue? Service: @Injectable() export class EventService { ...

"Using an indexer in TypeScript allows for direct access to object properties by simply specifying the key within

I have a requirement to access an object property using a string as the key interface MyObject { prop1: string; prop2: string; prop3: string; prop4: string; prop5: string; } let initialValues: MyObject; //I initialize some properties initialVa ...

Tips on showcasing an array as a matrix with a neat line property

I am currently developing an application using TypeScript, and utilizing a JSON array structured like this: data = [{"name":"dog", "line":1}, {"name":"cet", "line":1}, ...

Create a unique custom design for your Mapbox GL control

When developing the website, we utilized Angular 8, Typescript, and SCSS. We are using mgl-map to display a map, and I wanted to create a custom control for it with unique styles. I added the custom control to the map using: const centerOnCoordinatesC ...