Determine data type from an array's literal representation

I created a function that takes an array and returns an object by setting keys using the values from the array.

Here's a simplified example:

// The input 'arr' will be an array of arrays containing either one or two strings
function createObject(arr) {
  const output = {}

  arr.forEach(value => {
    // If there are two strings in the array, use the second one as the key
    const key = value.length === 1 ? value[0] : value[1]

    // Always use the first string as the value
    const val = value[0]

    output[key] = val
  })

  return output
}

For example, calling

createObject([['a', 'orange'], ['b'], ['c', 'apple'])
will result in
{ orange: 'a', b: 'b', apple: 'c' }
with type
{ orange: string, b: string, apple: string }
, which we'll refer to as type R. Please note that I am not concerned about the values of R being literals. Using the more general string type is sufficient for my needs.

In Typescript, I aim to infer the return type R based on a generic input T.

I can define T like this:

function createObject<T extends Array<[string, string?]>(arr: T) { ... }

Is it possible to derive R from T?


Update

An issue I foresee when trying to achieve this in Typescript is that R would need to consider the order of values in

T</code, which could lead to duplicate keys.</p>
<p>This challenge deviates from the simpler generic object type -> generic object type mapping syntax <code>[K in keyof T]: T[K] extends Thing ? ... : ...
.

If this proves to be problematic, but there is a potential workaround to handle it only in cases where there are no duplicate keys, that approach would suffice for my requirements.

Answer №1

It's absolutely possible:


type Values<T> = T[keyof T];

type Tuple<K, V = 1> = [K, V?]

type Elem = Tuple<any, any>

/**
 * Derives all elements in a tuple and converts it to an object
 */
type Predicate<T> =
    T extends Tuple<infer K, infer V>
    ? V extends PropertyKey
    ? Record<V, K>
    : K extends PropertyKey
    ? Record<K, K>
    : never
    : never

/**
 * Iterates through the input arguments
 */
type Reducer<
    Arr extends ReadonlyArray<Tuple<any, any>>,
    Result extends Record<string, any> = {}
    > = Arr extends []
    // final step of iteration
    ? Result
    // if there are still tuples in the array
    : Arr extends readonly [infer H, ...infer Tail]
    ? Tail extends ReadonlyArray<Tuple<any, any>>
    ? H extends Elem
    // recursively calls utility type and generates record type using predicate
    ? Reducer<Tail, Result & Predicate<H>>
    : never
    : never
    : never

// infers each key and property for accurate results
declare function foo<K extends string, V extends string, Tuples extends Tuple<K, V>[]>(tuples: [...Tuples]): Reducer<[...Tuples]>

foo([['a', 'orange'], ['b'], ['c', 'apple']]) // Record<"orange", "a"> & Record<"b", "b"> & Record<"apple", "c">

Try it out!

No necessity for const assertion here

For more examples, head over to my blog

Learn how to infer other data structures from function arguments here

Answer №2

Here is a version of the code that utilizes the function argument declared as as const:

type UnionToIntersection<U> = 
  (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never

type Result<T extends readonly [string] | readonly [unknown, string]> = UnionToIntersection<
    T extends readonly [infer V] ? { [k in Extract<V, string>]: V } :
    T extends readonly [infer V, infer K] ? { [k in Extract<K, string>]: V } : never
>

Check out the playground link for this code snippet

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 global CSS styles in Angular are not being applied to other components as expected

Currently utilizing Angular v10, I have a set of CSS styles that are meant to be used across the entire application. To achieve this, I added them to our global styles.css file. However, I'm encountering an issue where the CSS is not being applied to ...

Avoid altering the background color when adjusting the scale view on an apex chart due to changes in graph data

I have developed Apexchart components for line charts that come with a date filter picker feature. This chart is interactive and changes dynamically based on the series data provided. function DisplayChart({ series, xaxis }: { series: any; xaxis?: any }) ...

How can a child class access this.props within a function that overrides a parent class's function?

I am trying to access this.props.childName in the child function, which is defined within the parent function. However, I am encountering a TypeScript compile error (Property 'name' does not exist...). Strangely, if I use this.props.parentName, i ...

Want to learn how to integrate React-pdf (@react-pdf/renderer) with TypeScript on NodeJS and Express JS?

I am encountering difficulties running React-Pdf (@react-pdf/renderer) with TypeScript on an Express JS server. I have attempted to use babel but encountered errors that I cannot resolve. build error error error You can find the Github repository for t ...

The issue of PrimeReact AccordionTab failing to render when enclosed within a custom component

I'm encountering an issue with my specialized FilterAccordionTab component, which is an extension of PrimeReact's (V8) AccordionTab. It appears that the content is not being displayed when using this custom component. However, everything function ...

There was an error in the CSS syntax in the production environment due to a missed semicolon

Trying to execute the npm build command "webpack --mode=production --config ./config/webpack.config.prod.js" on our project results in an issue. The issue arises when I include the bootstrap file in my tsx file as shown below. import bs from "../../../../ ...

It is essential for the object to contain a method called '[Symbol.iterator]()' which will yield an iterator upon invocation

Currently, I am facing the following error: error TS2488: Type 'Usuario' must have a '[Symbol.iterator]()' method that returns an iterator. This is my code: usuarios.reducers.ts export interface UsuarioState { users: Usuario[]; ...

Retrieving User's Theme Preference from Local Storage in Next.js Instantly

As mentioned in various other responses, such as this one, Next.js operates on both the client and server side, requiring a guard to properly fetch from localStorage: if (typeof localStorage !== "undefined") { return localStorage.getItem("theme") } else ...

Enhancing JSON Formatting with Angular 4 and Typescript

In the process of developing my Angular 4 application, I am interfacing with a REST API through JSON requests. As I work on creating JSON objects to send via POST requests, I find myself putting in quite a bit of manual effort to construct them... I KNOW ...

There was a problem with the module '@angular/material' as it was unable to export a certain member

In creating a custom Angular Material module, I have created a material.module.ts file and imported various Angular Material UI components as shown below: import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/commo ...

Angular error: "name not found", element is not present in the DOM yet

In my current Angular5 project, I am implementing a small chat system. One issue I encountered is that when a user sends a message, a LI element is dynamically created: chat-component.html <li #ChatListItem *ngFor="let message of messages" class="list ...

Exploring the visitor design pattern with numerical enumerated types

I am exploring the use of the visitor pattern to ensure comprehensive handling when adding a new enum value. Here is an example of an enum: export enum ActionItemTypeEnum { AccountManager = 0, Affiliate = 4, } Currently, I have implemented the fol ...

Typescript throws an error when Redux useSelector fails to properly infer the state

Seeking guidance on how to access my state using the useSelector hook import { applyMiddleware, createStore } from 'redux'; import thunk from 'redux-thunk'; import { reducers } from './reducers'; export c ...

There are three possible interfaces for a functional component using React and Typescript

My goal is to develop a component that can utilize one of three interfaces based on the props passed to it. interface CommonProps { label: string; icon?: React.ComponentType; role?: string; } interface ButtonProps extends CommonProps { handleOnCl ...

Creating a declaration file for a library's entry point involves outlining the structure and types

I have developed an npm library that is made up of several ES6 modules, which are then consolidated into a single js file. The directory structure looks like this: src main.ts one.ts two.ts three.ts types index.d.ts index.ts The index.ts fil ...

Utilize React Styled Components to seamlessly unify the styles of two different components

I want to have consistent styles for both a styled input element and a styled select element. Currently, I accomplish this using string interpolation: const styles = ` background-color: white; width: 100%; border: 0 solid transparent; bor ...

What is the process for converting an observable array into a regular array and then retrieving that transformed array?

I'm currently attempting to convert an observable array into a regular array and then return the new array using the spread operator within the `get` function. I initially tried manually converting the observable array before subscribing with the map ...

Troubleshooting the failure of chaining functions in Angular2 during an HTTP request

I want to organize functions based on their specific roles in the code Here's the situation: when I'm making an http request, I want to separate the function that handles attaching the access token and headers from the one responsible for actual ...

What causes a merge conflict to occur within a React and Typescript project?

I stumbled upon this code snippet in our React/Typescript - JSX/TSX project with an unresolved Git merge conflict. Interestingly, the code successfully compiles and runs in the browser! Upon transpilation, I noticed that the top element is recognized as t ...

Failure of Ngx-translate to propagate to subcomponents

Since I have implemented my translate module in the shared/header.module.ts file, it mainly serves the purpose of handling language switching for the entire application. In header.module.ts: @NgModule({ imports: [ TranslateModule.forRoot({ lo ...