Dependencies of generic types among function arguments

Exploring the implementation of binding in a game engine, I aim to incorporate a touch of typing.

/** Engine external functions  */
export type Message<TBody> = { }

// This function returns the same unique object for the same `s`
// An internal engine object solely for comparison from user code
declare function Message<TBody>(s: string): Message<TBody>;

declare function sendMessage<B>(message: Message<B>, body: B): void;

/** User code */

// Messages created by the user at initialization
const MessageMove: Message<{x: number, y: number}> = Message("move");
const MessageHit: Message<{damage: number}> = Message("hit");

// Correct resolution of types in sendMessage
sendMessage(MessageMove, {});
sendMessage(MessageHit, { damage: 100 });

// Numerous implementations required for the `recieveMessage` function
// Unsure how to tie body type to message without boilerplate code
class MyActor {
    recieveMessage<B>(message: Message<B>, body: B) {
        if (message === MessageMove) {
            // Type of body is `B`, expected `{ x: number, y: number }`
            console.log(body.x, body.y);        
        } else if (message === MessageHit) {
            // Also expecting `B`
            console.log(body.damage);
        }
    }
}

Although the declaration types can be altered, message and body must remain separate entities

Various tricks have been attempted but with no successful outcomes.

The current solution involves extensive function declarations each time.

recieveMessage(message: "a"|"b"|"c", body: message extends "a" ? ... : message extends "b" ...

Alternatively, long overloads could be used in the function.

Answer №1

To implement a TypeScript discriminated union type, one effective method is using the following approach:

enum NotificationType {
    Success,
    Error,
}
type SuccessNotification = {
    type: NotificationType.Success,
    message: string
}

type ErrorNotification = {
    type : NotificationType.Error,
    errorMessage: string
}
type NotificationAction = SuccessNotification | ErrorNotification;

declare function sendNotification(notification: NotificationAction): void;

class NotificationHandler {
    handleNotification(notification: NotificationAction) {
        switch(notification.type) {
            case NotificationType.Success:
                console.log('Success:', notification.message);
                break;
            case NotificationType.Error:
                console.error('Error:', notification.errorMessage);
                break;
            default:
                throw new Error('Invalid notification type')
        }
       
    }
}

For further information and insights, you can delve into this resource.

Answer №2

In order to maintain separate the message and body, it is necessary to establish a "lookup" from the body using a key in the message.

type TBody = 'A' | 'B';

type Message<TBody> = { 
    name: TBody
}
type DataSchema = {
  A:
  {
    body:{
      x: number, y: number
  }
  },
  B: {
    body:{
      damage: number
  }
  },
}
type PropertyType<T, Path extends string> = Path extends keyof T ? T[Path] : never;
type lookup<T, Key extends keyof T, prop extends string> = PropertyType<T[Key], prop>;

Subsequently, the receiveMessage method can be implemented as such:

recieveMessage<TBody extends keyof DataSchema>(message: Message<TBody>, body: lookup<DataSchema, TBody, 'body'>) {

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 process of inserting data into MongoDB using Mongoose with TypeScript

Recently, I encountered an issue while trying to insert data into a MongoDB database using a TypeScript code for a CRUD API. The problem arises when using the mongoose package specifically designed for MongoDB integration. import Transaction from 'mon ...

Angular Update Component on Input ChangeEnsuring that the component is automatically

<div class=" card-body"> <div class="row"> <div class=" font-icon-list col-lg-2 col-md-3 col-sm-4 col-xs-6 col-xs-6" routerLinkActive="active" *ngFor="let subject of subjects"> <div class=" fon ...

Steps for importing a CommonJS module that exports as a callable into TypeScript

I am dealing with a project that has a mixture of JavaScript and TypeScript files. Within the project, there is a JS library that follows this structure: module.exports = () => { // logic dependent on environment variables // then... return { ...

Modifying the version target of TypeScript code results in the TypeScript Compiler being unable to locate the module

After installing signalr via npm in Visual Studio 2019, I encountered an issue. When the target in my compiler options is set to ES6, I receive the error TS2307 (TS) Cannot find module '@microsoft/signalr.'. However, when I change the target to E ...

Mastering VSCode IntelliSense: Unleashing the Power of Type Declarations

In my JavaScript projects, I aim to include TypeScript types sparingly to leverage IntelliSense for better code completions and receive warnings about type-related issues. To set up typechecking in JS, I created a jsconfig.json file and rely mostly on JSD ...

Unsynchronized state of affairs in the context of Angular navigation

Within my Angular project, I am currently relying on an asynchronous function called foo(): Promise<boolean>. Depending on the result of this function, I need to decide whether to display component Foo or Bar. Considering my specific need, what woul ...

Dynamically divide canvas screens based on fabricjs dropdown selection

I am attempting to implement split screens in fabric js, such as 1, 2, 4, 8, and 16. The screen should split based on the selection from the dropdown menu. Check out my current code where I have successfully uploaded images. If I click on the images, th ...

What potential drawbacks come with utilizing the OOP approach in both JavaScript and React?

While working on an internal project, I found myself creating a base system and implementing a custom form structure using TypeScript with an OOP approach. class Form extends React.Component {} abstract class FormElement extends React.Component<{valid ...

What is the most efficient way to use map-reduce in TypeScript to filter a list based on the maximum value of an attribute?

Recently, I came across a list that looked something like this: let scores = [{name: "A", skills: 50, result: 80}, {name: "B", skills: 40, result: 90}, {name: "C", skills: 60, result: 60}, {name: "D", skills: 60, ...

Encountering issues with MediaSession.setPositionState() and seekto functionalities not functioning properly

Having trouble with MediaSession.setPositionState() not displaying the audio time and seekbar not behaving as expected. const sound= document.querySelector('sound'); function updatePositionState() { if ('setPositionState' in navigato ...

Definition file for Typescript Angular 1.5 component

Encountering a problem with typescript and angular 1.5 - when building, an error pops up saying error TS2339: Property 'component' does not exist on type 'IModule'.. Could it be that I overlooked a definition file containing this proper ...

Utilize JSX attributes across various HTML elements

I'm looking for a solution to efficiently add JSX attributes to multiple elements. Here are the example attributes I want to include: class?: string; id?: string; style?: string; And here are the example elements: namespace JSX { interface Int ...

Create typings for object properties in TypeScript

I am inexperienced with TypeScript and am looking to set up types for my object keys. I have explored a few methods to accomplish this, but I am encountering an issue where an error is not triggered when assigning a value of a different type. For example: ...

A guide on showcasing nested arrays data in an Angular application

info = [ { list: [ { title: 'apple'} ] }, { list: [ { title: 'banana'} ] } ] My goal here is to extract the list items. Here is how they are structured. desired r ...

Essential typing techniques required for manipulating data retrieved from GraphQL

My headless CMS is responsible for generating all types in my GraphQL schema. Upon querying, I receive a result that contains an array which I can manipulate. However, when attempting to use filter, map, or find methods on the returned array, an error me ...

I encountered an issue with my TypeScript function in Angular, as it is unable to process multiple uploaded files

I'm having trouble with my TypeScript function in Angular that is unable to read multiple uploaded files. fileUpload(event: Event) { const self = this; this.imageUploadInp = event.target as HTMLInputElement; this.imageUploadInp.addEventLis ...

Utilizing global enumerations within VueJS

Is there a way to effectively utilize global enums in Vue or declare them differently? My current setup is as follows: Within my types/auth.d.ts: export {}; declare global { enum MyEnum { some = "some", body = "body", o ...

Using GraphQL to set default values in data within a useEffect hook can lead to never

Here's the code snippet that I'm working with: const [localState, setLocalState] = useState<StateType[]>([]); const { data = { attribute: [] }, loading } = useQuery<DataType>(QUERY, { variables: { id: client && client.id ...

Is it possible to establish a range (minimum/maximum) for a column during the creation of a PostgreSQL table

When using the table creation script in Flyway, I am curious if there is a way to specify a range or min/max value for a column. Here is the script: CREATE TABLE IF NOT EXISTS room ( // ... temperature NUMERIC NOT NULL, room_no VARCHAR( ...

How can I set up TypeScript warnings in Visual Studio Code to display as errors?

Take this scenario for instance async function a() { await null; } In VS Code, there is a minor warning about using await: 'await' has no effect on the type of this expression. ts(80007) Is there a way to elevate that warning to an error in b ...