Combine two categories to create a new type of entity?

Imagine having the following types:

type keys = ['name', 'age', 'height']
type valueTypes = [string, number, number]

Now I am wondering how to create a zipped object type like this:

{name: string, age: number, height: number}
?

My solution so far is:

type Zip<K, V> = {
    [KK in keyof K]: {
        [VV in keyof V]: K[KK] extends string ? Record<K[KK], V[VV]> : never
    }
}

Answer №1

When we talk about "zip," it usually refers to a convolution, which involves converting ordered pairs (or triplets, etc.) of arrays into an array of ordered pairs (or triplets, etc.). For examples, you can check out Python's zip() or Haskell's zip.

If we want to zip tuple types in TypeScript without considering cases where the tuples have different lengths, here is one way to do it:

type ZipTuple<T extends readonly any[], U extends readonly any[]> = {
  [K in keyof T]: [T[K], K extends keyof U ? U[K] : never]
}

This uses the mapped tuple/array feature introduced in TS3.1.

The result is as follows:

type Keys = ['name', 'age', 'height']
type Values = [string, number, number]

type KVTuple = ZipTuple<Keys, Values>;
// type KVTuple = [["name", string], ["age", number], ["height", number]]

With this information, we can define KeyValTuplesToObject<K, V>, which takes a tuple K of keys and a tuple V of values of the same length to produce an object type where each key corresponds to the value:

type KeyValTuplesToObject<K extends readonly PropertyKey[], V extends readonly any[]> =
  ZipTuple<K, V>[number] extends infer Z ?
  [Z] extends [[any, any]] ? { [P in Z[0]]: Extract<Z, [P, any]>[1] } : never : never

This first uses ZipTuple to convert the keys and values tuples into key-value pairs, then creates a mapped type that extracts the corresponding value for each key from these tuples.


In TS4.1, there is an update for KeyValTuplesToObject using key remapping in mapped types:

type KeyValTuplesToObject<K extends readonly PropertyKey[], V extends readonly any[]> =
  { [T in ZipTuple<K, V>[number]as T[0]]: T[1] };

Using the same Keys and Values as before, we get:

type KVObject = KeyValTuplesToObject<Keys, Values>
/* type KVObject = {
    name: string;
    age: number;
    height: number;
} */

Seems like everything is working well. Hopefully, this explanation was helpful!

Link to code

Answer №2


define Shift<T extends any[]> = ((...args: T) => any) extends ((first: any, ...rest: infer R) => any) ? R : never;

define ConvertToDictionary<Keys extends any[], Values extends any[]> = _ConvertToDictionary<Keys, Values, {}>
define _ConvertToDictionary<Keys extends any[], Values extends any[], Acc extends {}> = {
  0: Acc,
  1: _ConvertToDictionary<Shift<Keys>, Shift<Values>, {
    [K in Keys[0]]: Values[0]
  } & Acc>
}[Keys['length'] extends 0 ? 0 : 1];

type x = ["apple", "banana", "cherry"]
type y = [5, 10, 15]
type z = ConvertToDictionary<x, y> // type z is { apple: 5, banana: 10, cherry: 15}

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

Class field type based on generics

Consider the code snippet below in the playground: type AvailableTypes = { 'array': Array<any>; 'string': string; 'object': object; } class Wrapper<T extends keyof AvailableTypes> { // Can be of ...

Tips for resolving issues with this end-to-end test

Issue arises when clicking on the "Add Rule" button as new "Search Term" and "Search textarea" fields are generated, but Protractor is unable to detect them. describe('Checking for two rules and search terms on "Add New Audience Rule" page', () ...

Indicate when a ReplaySubject has reached its "completion" signal

I'm currently looking for an effective way to indicate when a ReplaySubject is empty. import {ReplaySubject} from 'rxjs/ReplaySubject'; const rs = new ReplaySubject<Object>(); // ... constructor(){ this.sub = rs.subscribe(...); } ...

unable to retrieve the values of the rowdata in GridOption

ngOnInit(): void { this.fetchAllAnimeReviews(); } public fetchAllAnimeReviews(){ this.animeservice.getAllAnimeReviews() .subscribe( response => { this.Anime = response; this.gridOption = this.createGridO ...

Implementation of a function in Typescript that can be defined with a

I am currently diving into the Typescript specification and I'm facing a challenge in creating a functional implementation for describable functions. https://www.typescriptlang.org/docs/handbook/2/functions.html The provided example lacks completene ...

Set up a new React 18 project with TypeScript using Create React App

Struggling with creating a React 18 app using TypeScript, I attempted to follow this guide but faced difficulties. Adding "types": ["react/next", "react-dom/next"] to the tsconfig file is giving me errors: Cannot find type def ...

Are you struggling with perplexing TypeScript error messages caused by a hyphen in the package name?

After creating a JavaScript/TypeScript library, my goal is for it to function as: A global variable when called from either JavaScript or TypeScript Accessible via RequireJS when called from either JavaScript or TypeScript Complete unit test coverage Th ...

React fails to acknowledge union types

I have the following types defined: export enum LayersItemOptionsEnum { OPERATOR, HEADER, } type sharedTypes = { children: string | ReactElement; }; type LayersItemStatic = sharedTypes & { label: string; option: LayersItemOptionsEnum; }; t ...

Utilize the ES6 spread operator to send information across components as props

ChildComponent.ts export class ChildComponent implements OnInit { @Input() name?: string; @Input() email?: string; // Many more properties constructor() {} ngOnInit(): void {} } ParentComponent.ts export class ParentComponent implements OnInit ...

The arrow function in Jest is missing a name property

Currently, my setup includes: node.js: 9.8.0 Jest: 23.4.2 ts-jest: 23.1.3 typescript: 2.9.2 While attempting the following in my *.test.ts files: const foo = () => 'bar'; console.log(foo.name); // '' foo contains the name pro ...

What is the best way to call an HTTP endpoint from a Docker container?

My NodeJS/TypeScript application (github repo) runs smoothly when I execute the script specified in package.json. For example, running npm run start allows me to access my local host and test endpoints using POSTMAN. However, I encountered an issue after ...

Exploring the world of dynamic routing in Angular using JSON data

I am facing an important query. Can we implement async routing in Angular 10? I have come across AsyncRoute in Angular2, but it seems to no longer exist in Angular 10. Here is a snippet of my code : getRoutes() { return this.http.get(this.APIROOT + &a ...

The compiler does not recognize the TSConfig option 'lib' - please review and correct

I have inherited an angular 1 project written in typescript version 1.8.10. However, I am encountering compilation issues with the following error message: Unknown compiler option 'lib' If I remove the "lib" line, a cascade of other errors suc ...

Is it feasible to return data when utilizing the ModalController in Ionic 5, specifically when executing a swipeToClose or backdropDismiss action?

With the latest update to Ionic 5's ModalController, a new feature allows users to swipe down on a modal to close it in addition to using the backdropDismiss parameter. Here is an example of how to enable this functionality: const modal = await this. ...

"Exploring the possibilities of integrating Typescript into Material-UI themes: A step-by

I'm experiencing some issues with Typescript pointing out missing properties in the palette section. Although adding //@ts-ignore resolves the problem temporarily, I would prefer to find a cleaner solution. As a newbie to Typescript, here is my attemp ...

Determine the data type of parameters in TypeScript

I'm currently working on creating a function that takes a class (Clazz) as a parameter and returns an instance of the same class, like this: function createInstance(Clazz) { ... return new Clazz(); } Is there a way to automatically determine ...

A destructured object with a Typescript interface

While working on a React / TypeScript project, I encountered an error involving destructuring an object. The issue arises when I try to destructure notificationData within the publish function. An error message stating "Property 'messages' does ...

Using vuex-class to interact with Vuex in non-Vue components

Is it possible to access Vuex outside of a Vue component using vuex-class? In a typical scenario, the process is quite straightforward: // some JS file import store from './../store'; // path to Vuex store store.commit('ux/mutationName&ap ...

Exploring The Depths of HOC with Enzyme and TypeScript

I have a higher-order component (HOC) that I need to test. During shallow mounting, I need to call some class methods: it('Should not call dispatch', () => { const dispatch = jest.fn() const WrappedComponent = someHoc(DummyComp ...

Building a unique React component with TypeScript that showcases a custom Grid item property

I'm attempting to display multiple items using a custom property for a Grid component. I'm unsure of the process for accomplishing this in a React component using TypeScript. export interface IComponentItem { width: 1 | 2 | 3 | 4 | 5 | 6 | 7 | ...