Generate a unique identifier based on data type

Is it feasible to create a function signature based on type in this manner? I have successfully created a function signature as shown below.

type Data = {
    update: boolean
};

type Distribution<T> = {
    on: <K extends keyof T>(key: K, listener: (value: T[K]) => void) => void;
    set: <K extends keyof T>(key: K, value: T[K]) => void;
};

const dataDistribution: Distrobution<Data> ...;

dataDistribution.set('update', true) // function signature has the key update and boolean as value

The concept is well explained in detail in this link.

However, I am facing a challenge when trying to reduce the second argument in cases where the type is

type Data = {
    update: void
};

or

type Data = {
    update: never
};

In such scenarios, how can I adjust the function signature to avoid errors like this?

dataDistribution.set('update') // Expected 2 arguments, but got 1

Is there a way to achieve this without making the second argument optional? Note that any solution involving the use of undefined is not acceptable in my case due to external constraints.

Answer №1

Through the ongoing discussion in the comments, a more efficient solution has emerged for addressing this particular issue. This insight was gained from this shared by jcalz.

It all starts with:

  • undefined is strictly prohibited
  • The function signature depends on the generic type

Initial Action

Replicated the Option setup from Rust.

This approach helps in eliminating undefined values for optional types.

export type Option<T> = {
    isNone(): boolean;
    isSome(): boolean;
    unwrap(): T;
    unwrapOr(alternativeOpt: T): T;
    expect(msg: string): T;
};

export const option = <T>(opt?: T): Option<T> => {
    const isNone = () => opt === undefined || opt === null;
    const isSome = () => !isNone();
    const unwrap = () => {
        if (isNone()) throw new Error('option is none');

        return opt as T;
    };
    const unwrapOr = (alternativeOpt: T) => {
        if (isSome()) return unwrap();

        return alternativeOpt as T;
    };
    const expect = (msg: string) => {
        if (isSome()) return unwrap();
        throw new Error(msg);
    };

    return { isNone, isSome, unwrap, unwrapOr, expect };
};

Next Step

Altered the type for the distribution implementation to align with the function signature depending on the generic type and transitioned options into the Option<T> structure.

This resolution was made achievable with insights from this

Credits to jcalz

export type DistributorOn<T> = <K extends keyof T>(
    key: K extends string ? K : never,
    listener: T[K] extends (...args: never[]) => void
        ? T[K]
        : (value: T[K] extends NonNullable<T[K]> ? T[K] : Option<Exclude<T[K], null | undefined>>) => void,
) => DistributorListener;

export type DistributorSend<T> = <K extends keyof T>(
    key: K extends string ? K : never,
    ...rest: T[K] extends () => void
        ? [value?: never]
        : T[K] extends (...args: never[]) => void
        ? Parameters<T[K]>
        : T[K] extends NonNullable<T[K]>
        ? [value: T[K]]
        : [value: Option<Exclude<T[K], null | undefined>>]
) => void;

export type Distributor<T> = {
    on: DistributorOn<T>;
    send: DistributorSend<T>;
};

Final Step

Test it out

type MyDistrobution = {
    name: string;
    age?: number;
    hook: () => void;
    msg: (subject: string, msg: string) => void;
};

const myDistro = distributor<MyDistrobution>();

// for MyDistrobution.name
myDistro.on('name', (name) => consoe.log(name); // <-- will be Marie Juana
myDistro.send('name', 'Marie Juana');

// for MyDistrobution.age
myDistro.on('age', (age) => console.log(age.isNone()); // <-- will be true
myDistro.send('age', option());

myDistro.on('age', (age) => consolelog(age.isSome())); // <-- will be false
myDistro.send('age', option(123));

// for MyDistrobution.hook
myDistro.on('hook', () => console.log('called'));
myDistro.send('hook');

// for MyDistrobution.msg
myDistro.on('msg', console.log); // <-- will be foobar, hello world
myDistro.send('msg', 'foobar', 'hello world');

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

Tips for defining a distinct series of key-value pairs in typescript

Having experience with a different language where this was simple, I am finding it challenging to articulate a sequence of pairs: Each pair is made up of two basic elements (such as strings or numbers) Each element may appear multiple times within the lis ...

Changing background color during drag and drop in Angular 2: A step-by-step guide

A drag and drop container has been created using Angular 2 typescript. The goal is to alter the background color of the drag & drop container while dragging a file into it. Typescript: @HostListener('dragover', ['$event']) public onDr ...

Guide to implementing an enum in an Angular Component

Having a global state (or "mode") in my Angular Components is leading me to look for more efficient ways to code. Here is what I have tried: @Component({ .... }) export class AbcComponent implements OnInit { enum State { init, view, edit, cre ...

ts-jest should replace the character '@' with the '/src' folder in the map configuration

I have set up a node project using TypeScript and configured various paths in my tsconfig.json file, such as: "paths": { /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl' ...

In Angular Google Maps, here's a step-by-step guide on how to zoom in

I am currently utilizing agm/core to showcase the coordinates of a map. Here is the code snippet I am using: <agm-map [latitude]="10.3207886" [longitude]="123.90250049999997"> <agm-marker [latitude]="10.3207886 [longitude]="123.90250049999997 ...

Easy-to-use blog featuring Next.js 13 and app router - MDX or other options available

Currently in the process of transitioning a Gatsby website to Next.js (v13.4) and utilizing the new App Router. However, I'm facing some challenges when it comes to migrating the blog section over because there isn't much accurate guidance availa ...

Exploring Nuxt's Getters with vuex-class for seamless data retrieval

Currently, I am in the process of developing an application using Nuxt and experimenting with vuex for the first time. Despite following numerous tutorials to set it up, I'm encountering difficulties accessing the store and suspect that the issues may ...

Tips for resolving the error message "Cannot use type 'string' to index type"

Currently, I am in the process of migrating a website from React to Next.js, and I am facing challenges when it comes to implementing i18n for SSR pages. I am following a tutorial provided at this link: I have already set up a lang folder and placed some ...

When compiling to ES5, TypeScript fails to remove imports

I am working on a TypeScript file that utilizes the moment library, and I need to import moment for it to compile properly. However, after compilation, the import line is still present in the compiled file, which is causing issues on my web page. Here is ...

Is there a way to create a universal getter/setter for TypeScript classes?

One feature I understand is setting getters and setters for individual properties. export class Person { private _name: string; set name(value) { this._name = value; } get name() { return this._name; } } Is there a w ...

Tips for preventing duplicate imports in Sass with the @use rule in Webpack

My sass modules have the ability to import each other as shown in the examples below: // LinearLayout.scss @mixin LinearLayout { ... } linear-layout { @include LinearLayout; } // ScrollView.scss @use "LinearLayout" as *; @mixin ScrollView { ...

In search of a practical and functional demonstration showcasing Electron v8 combined with TypeScript

Excuse the straightforwardness of my inquiry, as I am reaching the limits of my patience. I am in search of a practical example demonstrating the use of Electron v8 and TypeScript. The example should be simple and functional, without the need for WebPack, ...

Leveraging angular2-material with systemjs for Angular2 development

After completing the TUTORIAL: TOUR OF HEROES on this link, I attempted to integrate angular2-material into my project. Unfortunately, I am having issues with the CSS not displaying correctly. Can anyone provide insight into what I may be missing or doing ...

SlidingPane header in React disappearing behind Nav bar

Here is the code snippet from my App.js file: export class App extends React.Component { render() { return ( <BrowserRouter> <NavigationBar /> <Routes /> </BrowserRout ...

Typescript's way of mocking fetch for testing purposes

I have a query regarding the following code snippet: import useCountry from './useCountry'; import { renderHook } from '@testing-library/react-hooks'; import { enableFetchMocks } from 'jest-fetch-mock'; enableFetchMocks(); i ...

Can dynamic string types be declared in Typescript?

Let's consider the following scenario: export enum EEnv { devint, qa1 }; export type TEnv = keyof typeof EEnv; export const env:Record<TEnv, {something:number}> = { devint: { something: 1, }, qa1: { something: 1, }, } Now, I ai ...

Angular version 12 (node:3224) UnhandledPromiseRejectionWarning: Issue encountered with mapping:

Trying to generate the translation file in my Angular project using the command ng extract-i18n --output-path src/translate, I encountered this error message: \ Generating browser application bundles (phase: building)...(node:3224) UnhandledPromiseRej ...

Is there a way to fetch a particular object from Firebase database based on its value using AngularFire2?

Here is the database I am working with: firebase database I am trying to retrieve a dish that has its 'featured' attribute set to true (dish.feature = true). Is it possible to do this directly from the database, or do I have to retrieve all di ...

Error: ngModel does not reflect dynamic changes in value

After calling a Spring service, I am receiving JSON data which is stored in the "etapaData" variable. 0: id: 266 aplicacao: {id: 192, nome: "Sistema de Cadastro", checked: false} erro: {id: 220, nome: "Falta de orçamento", checked: false} perfil: {id: 8, ...

Clickable Angular Material card

I am looking to make a mat-card component clickable by adding a routerlink. Here is my current component structure: <mat-card class="card" > <mat-card-content> <mat-card-title> {{title}}</mat-card-title> &l ...