Restricting event categories with Typescript and @octokit/webhooks.js using `EmitterWebhookEventName`

I'm currently working with the @octokit/webhooks.js package that comes with advanced generic type constraints.

An example of how it is used:

webhooks.on('issue_comment.created', handlers.onIssueCommentCreated);

The function signature for this usage is defined as follows:

on: <E extends EmitterWebhookEventName>(event: E | E[], callback: HandlerFunction<E, TTransformed>) => void;

I am attempting to create a helper wrapper function around the on function like the one below, but I am encountering issues:

function addWebhook<
  E extends (EmitterWebhookEvent<EmitterWebhookEventName> & {
    payload: { sender: User };
  })['name'] &
    `${string}.${string}`
>(webhooks: Webhooks, event: E | E[], callback: HandlerFunction<E, unknown>) {
  webhooks.on(event, (options) => {
    options.payload.sender; // <-- error: TS2339: Property 'sender' does not exist on type 'EventPayloadMap[Extract ]'.
  });
}

My goal is to restrict this function to only accept values of E that are constrained to be of type EmitterWebhookEventName, and such that

EmitterWebhookEvent<E>['payload']['sender']
is of type User. I am specifically facing challenges in implementing the 'such that' logic.

The key library types can be found here:

export declare type EmitterWebhookEventName = typeof emitterEventNames[number];
export declare type EmitterWebhookEvent<TEmitterEvent extends EmitterWebhookEventName = EmitterWebhookEventName> = TEmitterEvent extends `${infer TWebhookEvent}.${infer TAction}` ? BaseWebhookEvent<Extract<TWebhookEvent, WebhookEventName>> & {
    payload: {
        action: TAction;
    };
} : BaseWebhookEvent<Extract<TEmitterEvent, WebhookEventName>>;
interface BaseWebhookEvent<TName extends WebhookEventName> {
    id: string;
    name: TName;
    payload: WebhookEventMap[TName];
}

Answer №1

Hey there! I'm the maintainer of the @octokit/webhooks library, and I'd be happy to assist you with resolving some of the issues you're facing.

Upon initial analysis, it seems that there is a compatibility issue with the generic E type. It ought to be a string that aligns with the EmitterWebhookEventNames, rather than an object.

Regarding the problem related to the sender property in the payload, the reason for the error lies in the fact that the sender property may not always exist in webhook payloads. This is due to the fact that when the E generic lacks a defined value, it defaults to all event names.

If you implement and utilize the function in this manner, you'll be able to ensure that the correct payload types are received in the callback for the specified event(s).

import { EmitterWebhookEvent, Webhooks } from "@octokit/webhooks";
import type { EmitterWebhookEventName, HandlerFunction } from "@octokit/webhooks/dist-types/types"
import type { User } from "@octokit/webhooks-types"

const webhooks = new Webhooks({secret: 'foo'});

function addWebhook<E extends EmitterWebhookEventName>(webhooks: Webhooks, event: E | E[], callback: HandlerFunction<E, unknown>) {
  webhooks.on(event, callback);
}

addWebhook(webhooks, "issue_comment.created", (options) => {
  options.payload.sender; // type is User
})

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 Datatable is displaying the message "The table is currently empty" despite the fact that the rows have been loaded through Angular

My experience with displaying a data table in Angular was frustrating. Even though all the records were present in the table, it kept showing "no data available." Additionally, the search function refused to work as intended. Whenever I tried searching for ...

I'm struggling to find a solution to this pesky TypeScript error that keeps popping up in the button component's styling. How can

An error related to style is appearing: <Button style = No overload matches this call. Overload 1 of 3, '(props: { href : string; } & { children?: React Node; classes?: Partial<Button Classes> | undefined; color?: "primary" | ...

Creating a dynamic image binding feature in Angular 8

I am working with an object array that requires me to dynamically add an image icon based on the type of credit card. Typescript file icon: any; savedCreditCard = [ { cardExpiryDateFormat: "05/xx", cardNumberLast: "00xx", cardId: "x ...

What is the best way to adjust the Material Drawer width in Reactjs to match the width of its children?

Currently, I am utilizing the Material-ui Drawer component to toggle the display of content on the right side of the screen. The functionality I am aiming for is that when the Drawer opens, it will shrink the existing content on the right without any overl ...

Unexpected token error on an optional property in Visual Studio Code

I encountered a problem with a project I cloned. Below is the code snippet created using this project: https://github.com/enuchi/React-Google-Apps-Script export interface Vehicle { wheels: number; insurance?: string; } export default class Car { whe ...

Why is type assertion required for TS array mapping?

This particular piece of code is causing an error: function myFilter(debts:Map<string, number>) : Map<string, number>{ return new Map([...debts] .map(d => [d[0], Math.round(d[1] * 10) / 10]) // error .filter(d => d[1] != 0) ) ...

Do not overlook any new error messages related to abstract classes

One of the challenges I'm facing is creating an instance of an abstract class within one of its functions. When using new this() in inherited classes, a new instance of the child class is created rather than the abstract class. Typescript throws erro ...

Updating the checkbox status in Angular when the radio button value is changed

I need help with a feature where all the checkboxes are checked based on the value of a radio button, and vice versa when an unchecked radio button is clicked. I have tried to implement this functionality using the following code but have not been successf ...

Performing an RxJS loop to retrieve the httpGet response, followed by executing httpPut and httpPost requests based

I am currently working on a UI form that allows users to update or add translation text. To achieve this, I need to create an rxjs statement that will perform the following tasks: Send an httpGet request to the database to retrieve translations in mult ...

Using JavaScript to round up the number

I need help rounding numbers in a specific way: Value Expected 0,523% 1% 2,235% 2,5% -0,081% -0,5% -1,081% -1,5% How can I achieve this using JavaScript? Possible Solution: static round (num) { const abs = Math.abs(num); const sign = num ...

The data structure '{ variableName: string; }' cannot be directly assigned to a variable of type 'string'

When I see this error, it seems to make perfect sense based on what I am reading. However, the reason why I am getting it is still unclear to me. In the following example, myOtherVariable is a string and variableName should be too... Or at least that&apos ...

A Comparison of Performance between If and Filter Operators in RxJS

Let's take a look at an example using RxJS. Type X: [utilizing filter] this.userService.afAuth.authState .pipe(filter(user => !!user)) .subscribe( _ => this.router.navigate(["/anything"]) ) Type Y: [utilizing if statement] this.u ...

Angular Error: The first argument has a property that contains NaN

Struggling with a calculation formula to find the percentage using Angular and Typescript with Angularfire for database storage. Encountered an error stating First argument contains NaN in property 'percent.percentKey.percentMale. The properties are d ...

Delete element from the array upon removal from the AutoComplete component

I am facing a challenge with the Material UI AutoComplete component in my project. The issue arises when I try to update the state of the associateList after clearing a TextField. Additionally, I would appreciate any guidance on how to handle removing an ...

Typescript Next.js Project with Custom Link Button Type Definition

I have a project that includes a custom Button component and a NextLink wrapper. I want to merge these two components for organization purposes, but when I combine the props for each, I encounter an issue with spreading the rest in the prop destructuring s ...

Establish a prototype attribute

From what I understand, TypeScript does not make a distinction between prototype properties and instance properties. Additionally, there is no built-in detection for Object.defineProperty on the prototype, which is unlike a feature for typechecking JavaScr ...

Utilizing the Solana Wallet Key for ArweaveJS Transaction Signing

Is there a method to import a Solana wallet keypair into the JWKInterface according to the node_modules/arweave/node/lib/wallet.d.ts, and then generate an Arweave transaction using await arweave.createTransaction({ data }, jwk);? Metaplex utilizes an API ...

Leveraging accessors in the Angular component's HTML template

I have a data model class called QuestionDataModel, like this: class QuestionDataModel { body: string; constructor(bodyValue: string) { this.body = bodyValue; } } In my component html template, I'm trying to display the bod ...

Dynamic getter/setter in Typescript allows for the creation of functions

I'm facing a challenge in making Typescript automatically infer types for dynamically created getter and setter functions. In my code, I have a class called MyClass which contains a map of containers: type Container = { get: () => Content s ...

The assertion error `args[3]` must be an integer value, but it failed to meet the requirement

Software Version: v12.19.0 Operating System Platform: Linux ayungavis 5.4.0-48-generic #52~18.04.1-Ubuntu SMP Thu Sep 10 12:50:22 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux Subsystem: Steps to Reproduce the Issue I attempted to follow the tutorial provided ...