Enigmatic Cartography Classification

In my attempt to construct a specialized Map-like class that maps keys of one type to keys of another, I encountered a challenge. A straightforward approach would be to create a Map<keyof A, keyof B>, but this method does not verify if the member types of each key align in both A and B.

I have initiated the implementation of the following class and contemplated enforcing type assertion to ensure that T is not never.

class KeyMap<A, B> {
  private mapping = new Map<keyof A, keyof B>();

  add<KA extends keyof A, KB extends keyof B, T = A[KA] & B[KB]>(
    a: KA,
    b: KB
  ): void {
    this.mapping.set(a, b);
  }
}

Although uncertain of its feasibility, I believe such mapping functionality could prove beneficial.

My overarching goal is to develop a versatile tool capable of seamlessly converting between A and B while ensuring type safety by validating all member type matches. If there exists a more effective approach to achieve this, I am open to alternative suggestions.

Answer №1

Disregarding your final inquiry about alternative methods, here is my approach to enforcing the requirement in the add() function where the b parameter must be limited to keys from B that have some intersection with the corresponding property in A from the a parameter:

type KeysWithOverlap<T, V> = { [K in keyof T]-?: (T[K] & V) extends never ? never : K }[keyof T];

class KeyMap<A, B> {
    private mapping = new Map<keyof A, keyof B>();

    add<KA extends keyof A, KB extends KeysWithOverlap<B, A[KA]>>(
        a: KA,
        b: KB
    ): void {
        this.mapping.set(a, b);
    }
}

The concept behind KeysWithOverlap<T, V> is to identify all keys of type T whose properties overlap with type

V</code. We refine the constraint on <code>KB
not just to keyof B, but to KeysWithOverlap<B, A[KA]>. Let's put it into action:

interface Foo {
    a: string;
    b: 3;
    c: boolean;
}

interface Bar {
    d: "str";
    e: number;
    f: boolean;
}

const km = new KeyMap<Foo, Bar>();
km.add("a", "e"); // error! e cannot be assigned to d
km.add("a", "d"); // valid
km.add("b", "e"); // valid
km.add("c", "f"); // valid

An error occurs when trying

add("a", "e")
due to incompatible types. However, other calls succeed because the types align.


Could this be optimized further?

Possibly not; perhaps the "no overlap" criterion isn't stringent enough. Should the focus be on types allowing you to read an A[KA] from a B[KB], or should it involve writing instead? In light of the Foo and Bar examples, caution is warranted if attempting to map between them since Bar["d"] exclusively accepts the specific string "str".

If contemplating a universal property-mapping function, individual key mappings may be elucidated in the type system, like so:

declare function convert<T, M extends {[K in keyof T]: PropertyKey}>(obj: T, keyMap: M): ConvertKeys<T, M>;
. While I could formulate such a function, it would deviate from the original query's essence.


Link to code demo on TypeScript Playground

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

Setting character limits when defining string variables in TypeScript

Upon reviewing the documentation, it appears that there is no straightforward method to perform type checking for the minimum and maximum length of a string data type. However, is there a possible way to define a string data type using custom types in ord ...

What could be causing the TypeScript exhaustive switch check to malfunction?

How come the default case in the switch statement below does not result in an exhaustive check where 'item' is correctly identified as of type 'never'? enum Type { First, Second } interface ObjectWithType { type: Type; } c ...

Utilize ngx-translate with an array as interpolation values

When working with ngx-translate, I use the instant method to translate messages into the user's language. These messages are provided as JSON objects and some of them contain dynamic values: { "message.key": "First value is {{0}} and se ...

Guide on integrating an element into a different element in a Vue 3 Tree Viewer

In my current setup, I've implemented a TreeView component that holds a tree. Each tree entry includes Children with their own unique label, perm, and further children. Take a look at an example of the tree: App.vue let tree = ref({ label: 'o ...

Enhancing Angular2 authentication with Auth0 for enabling Cross-Origin Resource Sharing

I have been working on implementing user authentication through Auth0. I followed the instructions provided on their website, but I am encountering authentication issues. Whenever I try to authenticate, an error message appears in the console stating that ...

How to have Angular open a PDF file in a new tab

Currently, I am working on implementing the functionality to open a PDF file in a new tab using Angular 9. The PDF file is received from an API as a blob. However, I have encountered an issue due to the deprecation of window.URL.createObjectURL(blob);. Thi ...

Event callback type narrowing based on the specific event key

While exploring different approaches to create a type-safe event emitter, I came across a pattern where you start by defining your event names and their corresponding types in an interface, as shown below: interface UserEvents { nameChanged: string; ...

Typedoc Error: Attempted to assign a value to an undefined option (mode)

After installing typedoc with the command npm install typedoc --save-dev, I proceeded to add typedocOptions to tsconfig.json: { "compileOnSave": false, "compilerOptions": { "baseUrl": "./", // ...some lin ...

What is the purpose of uploading the TypeScript declaration file to DefinitelyTyped for a JavaScript library?

After releasing two JavaScript libraries on npm, users have requested TypeScript type definitions for both. Despite not using TypeScript myself and having no plans to rewrite the libraries in TypeScript, I am interested in adding the type definition files ...

I'm having trouble with TypeScript locating a method specified in a parent class's type definition. What might be causing this issue?

While working with JointJS, I came across the defined typings. In my Typescript class, the structure is as follows: namespace joint { namespace shapes { namespace devs { class Model extends basic.Generic { ... } } } } ...

What is the general consensus on combining SWR with Redux - is it considered a best practice?

I am currently incorporating both SWR and Redux into my code. Here is how I'm using them together: const vehiclesStates = useSelector(({ vehiclesStates: state }) => state); // REDUX const response = useFetch("/vehicles/states") // SWR con ...

Ways to Halt observable.timer in Angular 2

As I work on Angular2's Component, I am currently implementing the following functions: export class MypageEditComponent { ngOnInit() { this.timer = Observable.timer(100, 100); this.timer.subscribe(t => { this.setFormData(); } ...

How can we enforce that only a certain type of ReactElement is allowed to be passed as props to a Component in TypeScript?

eslint and vscode seem to have trouble detecting validation errors when passing incompatible ReactElement types. Despite searching through documentation and examples, I haven't been able to find a solution that works. // Footer.tsx export interface ...

Tips on verifying the presence of a child in a Firebase list using AngularFire2

I am working on checking the existence of a Firebase list using AngularFire2 in Angular. Below is my parent list: I specifically need to verify if the child element Messages exists within this list. This is the code snippet I have so far: checkChild( ...

Tips for implementing a real-time search feature in Angular

I require assistance. I am attempting to perform a live search using the following code: when text is entered into an input, I want my targetListOptions array, which is used in a select dropdown, to update accordingly. The code runs without errors, but not ...

Detecting Typescript linting issues in VSCode using Yarn version 3.2.3

Every time I try to set up a new react or next app using the latest yarn v3.2.3, my VS Code keeps showing linting errors as seen in the screenshot below. The main error it displays is ts(2307), which says Cannot find module 'next' or its correspo ...

Disable a tab or menu item if the bean's boolean value is 'false'

I designed a home page with various menu/tab options that redirect the user when clicked, but only if they have access to that specific item. If access is not granted, the menu item becomes unclickable. While this functionality works, I am interested in en ...

Executing a dual ajax request in Angular 5

I am attempting to perform two HTTP requests consecutively, with the second request depending on the result of the first. However, it seems like I am overlooking something: getParkingSpots(date) { var gmt = this.getTimezone().subscribe(data=>{ if(d ...

Is it considered appropriate to return null in a didReceiveResponse callback function?

In my implementation, I have a callback called didReceiveResponse within a class that extends RESTDataSource. In this method, I return null when the response status is 404. However, due to the typing definition of RESTDataSource.didReceiveResponse, it seem ...

Dealing with a multi-part Response body in Angular

When working with Angular, I encountered an issue where the application was not handling multipart response bodies correctly. It seems that the HttpClient in Angular is unable to parse multipart response bodies accurately, as discussed in this GitHub issue ...