Bringing together a collection of objects connected by shared array elements

Given the types defined as:

type A = { commonKey: { a: string }[] };
type B = { commonKey: { b: number }[] };

Is it possible to create the type below without explicitly specifying commonKey?

type C = { commonKey: { a: string, b: number }[] }

My initial attempt with type C = A & B did not give the desired result:

const c: C = // ...
c.commonKey.map(x => x.a) // This works for `a` but not for `b`

I am looking for a generic solution that can combine the types regardless of the key name:

type ArrayContent = A['commonKey'][number] & B['commonKey'][number]
type C = { commonKey: ArrayContent[] };

Background

Using TypeScript 4.1 template literal types and recursive conditional types, I am enhancing the types for Elasticsearch queries in our project. We have a predefined type for the documents in our Elasticsearch cluster:

interface Post {
  id: string;
  title: string | null;
  author: {
    name: string;
  };
  comments: {
    id: string;
    message: string;
  }[];
}

At runtime, Elasticsearch allows limiting the fields to retrieve using paths, without distinguishing between keys within an array or a regular object.

const sourceFields = ['id', 'author.name', 'comments.message'];

I am developing a new type that, based on the document type and source fields, will determine the retrieved fields. Here is the current progress:

// TypeScript code snippet provided
// Usage example showing the type inference

It works well for handling undefined or null values and nested objects. However, when attempting to retrieve multiple fields within an array (

['comments.id', 'comments.message']
), the mentioned issue arises. Any suggestions for a solution?

Answer №1

Here is some helpful information:

type Base = { commonKey: unknown[] }

// credit to https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type
type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

type A = { commonKey: { a: string }[] };

type B = { commonKey: { b: number }[] };

/**
 * Steps to make magic happen:
 * 1) iterate through unions of array elements [A, B]
 * 2) T[number]['commonKey'][number] -> extract element from commonKey array
 * 3) UnionToIntersection<T[number]['commonKey'][number]> -> convert union to Intersection 
 * 4) ReadonlyArray<UnionToIntersection<T[number]['commonKey'][number]>> -> array of intersected elements
 */
type MakeMagic<T extends ReadonlyArray<Base>> = {
  [P in keyof T[number]]: ReadonlyArray<UnionToIntersection<T[number]['commonKey'][number]>>
}

type Result = MakeMagic<[A, B]>
/**
 * 
 type Result = {
    commonKey: readonly ({
        a: string;
    } & {
        b: number;
    })[];
}
 */

For more information on UnionToIntersection, you can visit this link

The assumption here is that commonKey is always an array

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

"Elaborate" Typescript Conditional Generic Types

Scenario I am currently working on implementing strong typing for the onChange function of a UI library's Select component. To provide some context, the existing type definition for their onChange is as follows: onChange?: (...args: any[]) => v ...

Arrange photos in a grid using React Dropzone uploaders

I'm having trouble figuring out how to create a custom preview similar to an image set in a grid using the react-dropzone-uploader component for uploading multiple files. Currently, when I upload images, they are displayed as shown in my normal uploa ...

TypedScript: A comprehensive guide to safely omitting deep object paths

Hi there, I have a complex question that I would like some help with. I've created a recursive Omit type in TypeScript. It takes a type T and a tuple of strings (referred to as a 'path'), then removes the last item on the path and returns t ...

Generating Legible JavaScript Code from TypeScript

I am looking to maintain the readability of my compiled JS code, similar to how I originally wrote it, in order to make debugging easier. However, the typescript compiler introduces several changes that I would like to disable. For instance: During compi ...

Traversing Abstract Syntax Trees Recursively using TypeScript

Currently in the process of developing a parser that generates an AST and then traversing it through different passes. The simplified AST structure is as follows: type LiteralExpr = { readonly kind: 'literal', readonly value: number, }; type ...

Steps to retrieve the value stored in a variable within an Angular service from a separate component

How can I effectively share question details and an array of options from one component to another using services? What is the recommended method for storing and retrieving these values from the service? In my question-service class: private static ques ...

Converting JSON responses from Observables to Arrays of objects in Angular

I have created a custom interface called Table which defines the structure of my data. export interface Table { id: number; title: string; status: string; level: string; description: string; } In my service, I am using HttpClient to se ...

Determining the name of the currently focused DOM element in Angular2

How can I detect the name of a selected element from a group of select elements on a page? For example: <select (click)="functionDetectName()" name="test1"> <select (click)="functionDetectName()" name="test2"> <select (click)="functionDete ...

Exploring Heroes in Angular 2: Retrieving Object Information by Clicking on <li> Items

Currently, I am delving into the documentation for an angular 4 project called "Tour of Heroes" which can be found at https://angular.io/docs/ts/latest/tutorial/toh-pt2.html. <li *ngFor="let hero of heroes" (click)="onSelect(hero)">{{hero.name}}< ...

The Route.ts file does not export any HTTP methods

Encountering an error while trying to migrate code from Next JS 12 with pages router in Javascript to Next JS 13 with TypeScript. ⨯ Detected default export in 'vibe\src\app\api\auth[...nextauth]\route.ts'. Export a name ...

Using TypeScript's conditional types for assigning types in React

I'm tasked with creating a component that can belong to two different types. Let's call them Type A = { a: SomeCustomType } Type B = { b: SomeOtherDifferentType } Based on my understanding, I can define the type of this component as function C ...

Automatically select the unique item from the list with Angular Material AutoComplete

Our list of document numbers is completely unique, with no duplicates included. I am attempting to implement a feature in Angular Material that automatically selects the unique entry when it is copied and pasted. https://i.stack.imgur.com/70thi.png Curr ...

Dealing with Error TS2769 in Visual Studio Code when passing props to a custom component in Vue 2 with Typescript

I've encountered an issue with a Vue JS component that involves passing a custom prop. I am utilizing the Vue Options API without utilizing the class component syntax. Whenever I pass ANY prop to my custom component the-header, I receive an error sta ...

In Javascript, what significance does the symbol ":" hold?

While exploring the Ionic framework, I came across the following code snippet: import { AlertController } from 'ionic-angular'; export class MyPage { constructor(public alertCtrl: AlertController) { } I'm curious about the significanc ...

The use of Next.js v12 middleware is incompatible with both node-fetch and axios

I am facing an issue while developing a middleware that fetches user data from an external endpoint using Axios. Surprisingly, Axios is not functioning properly within the middleware. Below is the error message I encountered when using node-fetch: Module b ...

Incorporating a Script into Your NextJS Project using Typescript

I've been trying to insert a script from GameChanger () and they provided me with this code: <!-- Place this div wherever you want the widget to be displayed --> <div id="gc-scoreboard-widget-umpl"></div> <!-- Insert th ...

Can TSLint and ESLint give a warning when a function is accessed as a property?

There have been instances where I've made this specific mistake, and I'm curious if there are any ESLint or TSLint rules in place that could detect it. if (this.isBrowser && this.imageURL) {.....} private isBrowser(): boolean{ retur ...

Guiding you on exporting a Typescript class with parameters in Node.js

Trying to find the Typescript equivalent of require('mytypescriptfile')(optionsObject); This is the TS code provided: export class Animal { name: string; public bark(): string { return "bark " + this.name; } constructor(color:string) ...

Tips for extracting year, month, and day from a date type in Typescript

I'm currently working with Angular 7 and I'm facing some challenges when it comes to extracting the year, month, and day from a Date type variable. Additionally, I am utilizing Bootstrap 4 in my project. Can anyone assist me with this? Below is ...

The reason behind the clickable issue with my duplicate <ion-select>

I've been working on a form using HTML, CSS, and JavaScript where I implemented an "Add" button to duplicate the form. The issue arises with the ion-select element within the duplicated form. While the original form displays all predefined options upo ...