Combining union types with intersection operators in Typescript

Here's a concept for an event handling system:

type EventMap = Record<string, any>;

type EventKey<T extends EventMap> = string & keyof T;
type EventReceiver<T> = (params: T) => void;

interface Emitter<T extends EventMap> {
  on<K extends EventKey<T>>
    (eventKey: K, fn: EventReceiver<T[K]>): void;
  emit<K extends EventKey<T>>
    (eventKey: K, params: T[K]): void;
}

// example usage
const eventHandler = eventEmitter<{
  data: Buffer | string;
  end: undefined;
}>();

// OK!
eventHandler.on('data', (x: string) => {});

// Error! 'string' is not assignable to 'number'
eventHandler.on('data', (x: number) => {});

The setup functions correctly but I have questions about it:

  1. Why is EventKey defined with string &?

  2. Do we really need generics K in the on and emit methods or could we simplify them by using just keyof T for the type of eventKey?

Thank you for your insights.

Answer №1

Why is it necessary to append a string & here?

The use of string & ... ensures that only keys of type string are valid for defining the EventKey type.

Despite EventMap being declared as Record<string,any>, it is still possible to assign an object with non-string properties:

const a: EventMap = {
  foo: 'bar',
  100: 'baz', // error, not a string property
}

Why does this happen? Because objects can have excess properties.

In such scenarios, string & keyof T would return never for properties that are not of type string.

Could eventKey parameter's type simply be keyof T?

This allows for keys defined as union types that extend string (Refer to this answer):

type EnumK = 'alpha' | 'beta'
type MyObject = {[P in EnumK]: string }

const obj: SomeObj = {
  alpha: 'foo',
  beta: 'bar'
}

In this case, the keys of obj represent an extension of string, specifically the union of string literal types alpha and beta. By using K extends ..., you capture this relationship and ensure type safety when indexing T[K].

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

Oh no! A critical mistake has occurred: Mark-compact operations are not working efficiently near the heap limit, leading to a failed allocation due to the

My project is not particularly complex, I only just started it. However, when I run the command npm run dev, node js consumes more than 4GB of memory and eventually crashes with a fatal error: --- Last few GCs --- [16804:0000018EB02350F0] 128835 ms: Mar ...

What is the proper way to specify the type of a mixed Map?

private readonly _subscriptions:???? = new Map([ ['a', new Map()], ['b', new Map()], ['c', new Map()], ['d', []] ]) No suitable type was found for this map. I tried vario ...

Passing parameters to an Angular 2 component

When it comes to passing a string parameter to my component, I need the flexibility to adjust the parameters of services based on the passed value. Here's how I handle it: In my index.html, I simply call my component and pass the required parameter. ...

Error in Angular: Trying to access the property 'id' of an undefined value

I am facing an issue with a div tag in my HTML file. The code snippet looks like this: <div *ngIf="chat.asReceiver.id != user?.id; else otherParty"> Unfortunately, it always returns the following error: ERROR TypeError: Cannot read propert ...

Next.js v13 and Firebase are encountering a CORS policy error which is blocking access to the site.webmanifest file

Background: I am currently developing a website using Next.js version 13 in combination with Firebase, and I have successfully deployed it on Vercel. Upon inspecting the console, I came across two CORS policy errors specifically related to my site.webmani ...

Link the ngModel input to an object within an ngFor iteration

Looking to create a dynamic form using an array that includes FieldLabel and DataModel references. I want to use the DataModel as an object reference, so when the user updates an input field, the referenced model is updated. I have searched extensively bu ...

Issue with data not being recorded accurately in React app after socket event following API call

The Application Development I have been working on an innovative app that retrieves data from the server and visualizes it on a chart while also showcasing the total value in a Gauge. Additionally, the server triggers an event when new data is stored in t ...

The outcome of using Jest with seedrandom becomes uncertain if the source code undergoes changes, leading to test failures

Here is a small reproducible test case that I've put together: https://github.com/opyate/jest-seedrandom-testcase After experimenting with seedrandom, I noticed that it provides consistent randomness, which was validated by the test (running it multi ...

Holding off on completing a task until the outcomes of two parallel API requests are received

Upon page load, I have two asynchronous API calls that need to be completed before I can calculate the percentage change of their returned values. To ensure both APIs have been called successfully and the total variables are populated, I am currently using ...

Utilize the useState() hook to add an object and display its data in a React Native

Here is a function I am working with: const [dataLoc, setDataLoc] = useState({date: "No data received yet from sensor", coords: {}}); This is where I set the location: Geolocation.getCurrentPosition( location => { const date = d ...

Local variables are now being refreshed with every modification in the data stored in Cloud Firestore

Having trouble maintaining the accuracy of my local variables in sync with changes to the data in cloud firestore. Specifically, in my local variable called count_vehicle, the value represents a count based on specific conditions from the data in cloud fir ...

Using string interpolation within the onclick event (Ionic 2 + Angular 2)

One question that's troubling me - I'm attempting to pass a string within an "onclick" event in my Ionic 2 app, but it keeps throwing an error. <button onclick="window.plugins.socialsharing.shareViaWhatsApp(null, null, '{{sound.file}}&a ...

What is the best way to interpret a line break within a string variable in TypeScript?

Realtime Data base contains data with \n to indicate a new paragraph. However, when this data is retrieved and stored in a String variable, the website fails to interpret the \n as a paragraph break: https://i.stack.imgur.com/tKcjf.png This is ...

Error encountered: "Unable to locate module 'typescript-Collections' when modifying the module to "umd" or "amd" in the tsconfig.json file."

I recently upgraded to VS17 Enterprise and encountered an issue when trying to import the TypeScript Collections library from GitHub. After following the instructions on their page, I realized that changing the module option in my tsconfig.json file to eit ...

Breaking down nested arrays in typescript

After receiving a response from the service, the data included the following: "rows": [ [ "stravi/aa", "202001", "59", "51", "2558.98", "0.5358894453719162", "1.9204668112983725", "140", "2.346630 ...

Migration unsuccessful due to incompatible peer dependencies detected - Updating Angular to Version 12 was not successful

Currently in the process of upgrading my Angular v11 apps to Angular v12. Encountering an error while attempting to upgrade Angular packages: ng update @angular/core@12 @angular/cli@12 Error: Migration failed due to incompatible peer dependencies The pa ...

When tests/** are not included in the tsconfig, the TS language features in Vscode become inaccessible

I am looking to configure my TypeScript tests in such a way that they receive linting, code completion, and VSCode intellisense (TypeScript language features) when the test folder is placed next to the src folder. However, I want to ensure that my tests do ...

Experimenting with a Collection of Items - Jest

I need to conduct a test on an array of objects. During the test coverage analysis of the displayed array, I found that the last object with the key link has certain conditions that are not covered. export const relatedServicesList: IRelatedServiceItem[ ...

Error: Unable to dispatch a Redux-thunk action with additional arguments in React

Currently, I am attempting to work with TypeScript alongside Redux-Thunk and Redux. My goal is to pass an object to any action (I am utilizing dependency injection and need to pass either an object service or a string). However, I encountered the followin ...

Dealing with compilation errors in TypeScript

I'm working on a simple TypeScript program that looks like this - const users = [{ name: "Ahmed" }, { name: "Gemma" }, { name: "Jon" }]; // We're trying to find a user named "jon". const jon = users.find(u => u.name === "jon"); However, wh ...