Using various types in TypeScript to represent a variety of events

If I have these three distinct objects:

{
  id: 1,
  time: 1000,
  type: 'A',
  data: { a: 'one' }
}

{
  id: 2,
  time: 1001,
  type: 'B',
  data: { b: 123 }
}

{
id: 3,
time: 1002,
type: 'C',
data: { c: 'three', d: 123 }
}

and need to define a function like this:

function createEvent<TData,TEvent>(id: number, time: number, TData data): TEvent {}

What should my types be in order to successfully implement createEvent? It seems that a combination of intersection, union, and generics will be needed.

For example, let's consider the following type definitions:

type InfoA = {a: string}
type InfoB = {b: number}
type InfoC = {c: string, d: number}
type EventTypeHeader = {id: number, time: number}
type EventTypeA = EventTypeHeader & { category: 'A', data: InfoA}
type EventTypeB = EventTypeHeader & { category: 'B', data: InfoB}
type EventTypeC = EventTypeHeader & { category: 'C', data: InfoC}
type CustomEvent = EventTypeA | EventTypeB | EventTypeC
function createEvent<T extends CustomEvent, U extends InfoA|InfoB|InfoC>(
 id: number, time: number, data: U): CustomEvent
{
 return {
  id: id
  time: time
type: 'something needs to go here?'
data: data
 }
}
 

I seem to be struggling with understanding when to use union types and how to effectively apply generic types.

Thank you!

Answer №1

The question isn't perfectly clear, but after examining your objects and the function, I believe I've correctly understood your needs.

One issue I encountered was that the buildEvent function didn't have a type argument that was visible in all objects. Consequently, I had to make changes so that the type serves as a discriminant for our union, indicating which data should be utilized.

Please consider the following solution:

// data + type
type TDataA = { type: 'A', data: { a: string } };
type TDataB = { type: 'B', data: { b: number } };
type TDataC = { type: 'C', data: { c: string, d: number } };

type TData = TDataA | TDataB | TDataC
type TBase = {
    id: number,
    time: number
}
type TEvent = TBase & TData; // Our event is a combination of Base and Type+Data

// Pay attention: the data argument has been renamed to typeAndData
function buildEvent<T extends TData>(id: TBase['id'], time: TBase['time'], typeAndData: T): TBase & T {
    return {
        id,
        time,
        ...typeAndData
    }
}

const eventA = buildEvent(1, 1000, { type: 'A', data: { a: 'a' } }); // Correct
eventA.data.a // Properly contains only 'a'

const eventAWrong = buildEvent(1, 1000, { type: 'A', data: { b: 'a' } }); // Error - Incorrect data

const eventB = buildEvent(1, 1000, { type: 'B', data: { b: 11 } }); // Correct

Finally, you may notice that I didn't explicitly use the TEvent type in the buildEvent function, but the type returned by buildEvent can be assigned to TEvent as they match structurally. Here's a demonstration:

type BuildEventIsTEvent = ReturnType<typeof buildEvent> extends TEvent ? true : false;
// The above evaluates to true

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

Differentiating Angular HTML and script code is a fundamental practice in Angular development

I am working on an angular frontend project that includes a dashboard component with a sidebar. The code currently has both the script tag and HTML code in the same file, making it difficult to manage. I am looking for a way to separate them to make the co ...

Console not logging route changes in NextJS with TypeScript

My attempt to incorporate a Loading bar into my NextJs project is encountering two issues. When I attempt to record a router event upon navigating to a new route, no logs appear. Despite my efforts to include a loading bar when transitioning to a new rout ...

Implement an interface with a specific number of properties in TypeScript

I am attempting to create a custom type that defines an object with a specific number of key-value pairs, where both the key and value are required to be numbers. Here is what I envision: type MatchResult = { [key: number]: number; [key: number]: numbe ...

A Guide to Implementing Inner CSS in Angular

I am working with an object named "Content" that has two properties: Content:{ html:string; css:string } My task is to render a div based on this object. I can easily render the html using the following code: <div [innnerHtml]="Content.html"& ...

Choosing specific information in Typescript response

I am encountering an issue with my HTML where it displays undefined(undefined). I have checked the data in the debugger and I suspect that there may be an error in how I am using the select data. Here is a snippet of the code: <div *ngIf="publishIt ...

Guide to Implementing Dependency Injection in Angular 2

When working with Angular Components written in TypeScript, it is possible to declare a class member (variable) as a parameter of the constructor. I am curious about the reason for doing so. To clarify, take a look at the examples below. Both achieve the ...

The resource in CosmosDB cannot be found

I have successfully stored documents on Cosmos, but I am encountering an issue when trying to retrieve them using the "read" method. this.cosmos = new CosmosClient({ endpoint: '' key: '' }); this.partitionKey = '/id' thi ...

Testing reactive streams with marble diagrams and functions

Upon returning an object from the Observable, one of its properties is a function. Even after assigning an empty function and emitting the object, the expectation using toBeObservable fails due to a non-deep match. For testing purposes, I am utilizing r ...

Ways to extract the final digit from a format such as an IP address in JavaScript

Is there a way to retrieve the last digits 192.168.1.180 For instance: From the IP address 192.168.1.180, I would like to extract 180. Thank you in advance ...

What is the maximum allowable size for scripts with the type text/json?

I have been attempting to load a JSON string within a script tag with the type text/json, which will be extracted in JavaScript using the script tag Id and converted into a JavaScript Object. In certain scenarios, when dealing with very large data sizes, ...

The swipe motion will be executed two times

By pressing the Circle button, the Box will shift to the right and vanish from view. Further details (FW/tool version, etc.) react scss Typescript framer-motion import "./style.scss"; import React, { FunctionComponent, useState } from &q ...

Troubleshooting Next.js Mobile Freeze Issue: Unresponsive Scroll After Page Transition

Encountered a strange bug while testing my Next.js + Bootstrap demo project on mobile view. When using the burger menu to navigate to a new page on a mobile phone, attempting to scroll down causes it to stick/freeze/hang inexplicably. Despite my efforts to ...

Exploring the capabilities of supertest by testing endpoints in Express with NodeJS

Having trouble setting up a test environment to test my TypeScript Express Node.js endpoints. Here are the packages I've installed: jest ts-jest jest-junit supertest @types/supertest @types/jest This is how my spec file looks like: imp ...

Maintaining scroll position in React Router v6

My website's homepage displays a user's feed. When a user clicks on one of the feed cards, they are taken to a different URL. However, when the user returns to the homepage, it automatically scrolls to the top. I am looking for a way to preserve ...

Pressing the confirm button will close the sweet alert dialog

When pressing the confirm button, the swal closes. Is this the intended behavior? If so, how can I incorporate the loading example from the examples? Here is my swal code: <swal #saveSwal title="Are you sure?" text ="Do you want to save changes" cancel ...

Experiencing unexpected behavior with Next.JS getStaticProps functionality

I am currently working on a website where I want to display dynamic feedback as cards. However, my fetchData variable within the Home function is always returning undefined. Here's the code snippet I have tried: import UserCard from "../component ...

When declaring an array of numbers in sequelize-typescript, it triggers a TypeScript error

In my application, I am working with PostgreSQL, Sequelize, Sequelize-TypeScript, and TypeScript. I have a need for a table called Role where each role has multiple permissions of type integer. I'm following the guidelines provided in the sequelize-ty ...

Encountering an error message in Angular stating "Spotify is not defined" while utilizing "@types/spotify-web-playback-sdk"

I'm currently integrating Spotify's web player SDK into an Angular project. I followed the steps outlined in this particular question and answer: Installing the types package, then including the types reference at the beginning of the file where ...

Transforming a function into an array in TypeScript

I attempted to use the map() function on a dataURL array obtained from the usePersonList() hook, but I am struggling to convert my function to an array in order to avoid errors when clicking a button. import Axios from "axios"; import React, { us ...

Problem with opening the keyboard feature in an Ionic app

Hello everyone, I'm relatively new to Ionic development and I've been trying to integrate a keyboard plugin into my application that opens from the footer and focuses on input fields for entering values. Here is the link to the plugin I used: ht ...