Converting Typescript Object Types to Array Types with Tuple Structures

Presently, I have the following:

interface Obj {
    foo: string,
    bar: number,
    baz: boolean
}

The desired outcome is to convert this interface into the tuple format below:

[string, number, boolean]

Is there a way to achieve this conversion?

Update:

I am encountering an issue with my current project: I am developing a library that follows a declarative approach, where users need to specify function parameters in an object literal. For example:

let paramsDeclaration = {
  param1: {
    value: REQUIRED<string>(),
    shape: (v) => typeof v === 'string' && v.length < 10
  },
  param2: {
    value: OPTIONAL<number>(),
    ...
  },
}

Subsequently, the library takes this object and generates a function with parameters based on it:

   (param1: string, param2?: number) => ...

Hence, creating such a function is not the main concern; rather, accurately typing it so that users can benefit from effective code completion (IntelliSense).

P.S. While I understand this may not be fully solvable, I am curious to explore potential workarounds or hacks.

Answer №1

Most of the time when you encounter a problem that seems impossible in Typescript, it's actually doable but not recommended.

I found a solution using TuplifyUnion mentioned in this post. This function converts a union type into a tuple type by starting with a union of the keys of an object, rather than its values, as values could also be unions like true | false.

If you want users to specify parameters for a function generated by your API, it's better to accept those specifications in an array from the beginning. Check out the linked answer for more insights on the // oh boy don't do this comment.

type ObjValueTuple<T, KS extends any[] = TuplifyUnion<keyof T>, R extends any[] = []> =
  KS extends [infer K, ...infer KT]
  ? ObjValueTuple<T, KT, [...R, T[K & keyof T]]>
  : R

// type Test = [string, number, boolean]
type Test = ObjValueTuple<Obj>

Check out the Playground Link

Answer №2

Although this may not directly address the original question, I want to offer some insights that could potentially be useful:

function MANDATORY<T>(): T {
    //...
}
function OPTIONAL<T>(): T {
    //...
}

interface ParametersDefinition {
    readonly [paramName: string]: {
        readonly value: any;
        readonly shape?: Function;
    };
}

type CustomFunction<T> = T extends {
    readonly [paramName: string]: {
        readonly value: infer U;
    };
} ? (...params: Array<U>) => void
    : never;

function generateFunction<T extends ParametersDefinition>(paramsDefinition: T): CustomFunction<T> {

    // ...
}

const definitionsOfParams = {
    parameter1: {
        value: MANDATORY<string>(),
        shape: (v: any) => typeof v === 'string' && v.length < 10
    },
    parameter2: {
        value: OPTIONAL<number>(),
        //...
    },
};
// The type is '(...params: (string | number)[]) => void'
const customFunc = generateFunction(definitionsOfParams);
customFunc('1', 2); // Okay
customFunc(2, '1'); // Also okay, but probably not intended
customFunc(Symbol()); // TypeScript error

Answer №3

Here are some different suggestions,
The parameters need to be ordered in a specific way.

interface Param {
    readonly value: any;
    readonly shape?: Function;
}
type OrderedFunction<T extends Record<string, Param>, orders extends (keyof T)[]> = (...args:{
    [key in keyof orders]:orders[key] extends keyof T ? T[orders[key]]['value']: orders[key];
})=>void;

function generateFunction<T extends Record<string, Param>, ORDERS extends (keyof T)[]>(params: T, ...orders:ORDERS): OrderedFunction<T, ORDERS> {
    return 0 as any;
}

const customFunc1 = generateFunction({a:{value:0}, b:{value:''}, c:{value:true}}, 'a', 'b', 'c');
customFunc1(0, '1', true); // works
customFunc1(true, 0, '1'); // gives an error

or
Parameter Declarations with array

type ArrayFunction<T extends Param[]> = (...args:{
    [key in keyof T]:T[key] extends Param ? T[key]['value'] : T[key]
})=>void;

function generateArrayFunction<T extends Param[], ORDERS extends (keyof T)[]>(...params: T): ArrayFunction<T> {
    return 0 as any;
}

const customFunc2 = generateArrayFunction({value:0}, {value:''}, {value:true});
customFunc2(0, '1', true); // works
customFunc2(true, 0, '1'); // gives an error

Answer №4

It appears that the general agreement is that it is currently not possible, but I wanted to give it a shot anyway. Because objects do not preserve key order, ordering cannot be guaranteed.

solution

export type Partition<T> = UnionToTuple<
  { [K in keyof T]: { [k in K]: T[K] } }[keyof T]
>

helpers

type Pack<T> = T extends any ? (arg: T) => void : never
type Unpack<T> = [T] extends [(arg: infer I) => void] ? I : never
type Into<T> = Unpack<Unpack<Pack<Pack<T>>>>

type UnionToTuple<T> = Into<T> extends infer U
  ? Exclude<T, U> extends never
    ? [T]
    : [...UnionToTuple<Exclude<T, U>>, U]
  : never

example

type Data = { a0: 'foo'; b0: { b1: 'bar' } }

type Mock = Partition<Data>
[
  { a0: 'foo'; },
  { b0: { b1: 'bar'; }; }
]

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

Navigating the world of TypeScript and SystemJS without the confines of Angular

Lately, I've been delving into TypeScript through research and simple "hello world" projects. However, I've hit a roadblock when it comes to using System.js with TypeScript that I just can't seem to overcome. Most tutorials and demos online ...

CreatePortalLink directs users to a payment URL instead of a dashboard

I am currently working on a project that utilizes the Stripe payments extension in conjunction with Firebase. The application is built using Next JS. After a user subscribes, I want to provide them with a tab where they can manage their subscription. The ...

What are the best ways to troubleshoot my Angular 2 project?

I've been searching for my TypeScript files in the console, but they're not showing up. I've tried everything to debug my Angular 2 project, but no luck. I can't move forward without debugging, can anyone lend a hand? ...

Show dynamic JSON data in a nested format on the user interface with Aurelia's Treeview component

In the visual representation provided, there are currently three objects in the array. These objects, referred to as "parents", each have their own set of "children". The complexity lies in the fact that a parent element can also serve as a child element w ...

Solving the problem of cookieParser implementation in NestJS

Greetings! I have a question for you. Currently, I am working on developing a backend using NestJS, a Node.js framework. Everything is functioning smoothly except for some issues when it comes to hosting. I have created a new NestJS project and made some ...

Translate Typescript into Javascript for use in React applications

Can anyone assist me in converting this Typescript code to Javascript? function ImageMagnifier({ src, width, height, magnifierHeight = 100, magnifieWidth = 100, zoomLevel = 1.5 }: { src: string; width?: string; height?: string; magnifie ...

What is the proper way to type the SubmitEvent so that the event.target.children property can be accessed

Below is the form I currently have: <form id="search" method="post"> <input type="text" name="query" id="search-field"/> </form> I am looking to add a submit event listener in TypeScript: ...

What is the best way to update this payload object?

Currently, I'm developing a route and aiming to establish a generic normalizer that can be utilized before storing user data in the database. This is the function for normalization: import { INormalizer, IPayloadIndexer } from "../../interfaces/ ...

Angular log out function to automatically close pop-up windows

Within my application, there is a page where users can open a popup window. When the user clicks on logout, it should close the popup window. To achieve this, I have used a static variable to store the popup window reference in the Global.ts class. public ...

Angular2 and ReactiveX: Innovative Pagination Strategies

Currently, I am diving into the world of ReactiveX. To make things easier to understand, I have removed error checking, logging, and other unnecessary bits. One of my services returns a collection of objects in JSON format: getPanels() { return this. ...

Angular 2 event emitter falling behind schedule

I am currently utilizing Angular 2 beta 6. The custom event I created is not being captured import {Component, OnInit, EventEmitter} from 'angular2/core'; import {NgForm} from 'angular2/common'; import {Output} from "angular2/core" ...

Is there a way to effectively eliminate an array of objects in JavaScript or TypeScript and alter the object structure simultaneously?

I am seeking solutions to restructure an object that has multiple arrays of objects so that I can access the object directly. console.log(value.data.summary[0].data) Instead of accessing arrays in this manner, I want to modify my data structure. Is there ...

The useAutocomplete function in Material-UI fails to consider the disabled

Currently, I am working on developing my own Autocomplete component by utilizing the useAutocomplete hook from the mui/base package. Most parts of the implementation are functioning correctly, except for the disabled option. My expectation is that the com ...

How can I display the top 5 data results after subscribing in Nativescript?

Utilizing this function allows me to retrieve all my data from the web service. public data: Data[]; getall() { this.ws.getalldata().subscribe( data=> { this.data= data; } ); } Here is a ...

JavaScript's Blob to Base64 conversion using FileReader is failing to produce any output

In my typescript application, I am utilizing the FileReader to convert a blob into a base64 image for display within the template. adaptResultToBase64(res: Blob): string { let imageToDisplay : string | ArrayBuffer | null = ''; const re ...

Embracing Interfaces Over 'any' Types in TypeScript

https://i.stack.imgur.com/W6NMa.pngWould it be beneficial to utilize an interface as a variable type rather than opting for any? For instance, if I have 3 functions where I am declaring variables that can contain alphanumeric data, would defining them us ...

typescript persist zustand: typing your store

Running a simple store interface CartState { cart: { [id: string]: CartDto }; addItem: ({ id, image, name, price }: Omit<CartDto, "quantity">) => void; removeItem: (id: string) => void; reset: () => void; } export const use ...

Utilizing React Typescript to Efficiently Manage Multiple Checkboxes within a List

I'm working on a scenario where I have to manage multiple checkboxes in a list Only one checkbox can be selected at a time For example, if I toggle on Checkbox 1 and then click on Checkbox 2 - I want to automatically toggle off Checkbox 1 as I toggl ...

Display a single unique value in the dropdown menu when there are duplicate options

Hey there, I'm currently working on retrieving printer information based on their location. If I have multiple printers at the same location, I would like to only display that location once in the dropdown menu. I am aware that this can be resolved at ...

Create a class with additional attributes to support different types of options

I define a set of options represented by strings: export type Category = 'people' | 'projects' | 'topics' | 'tools' An index is declared as follows: interface Entry { ... } type IPostEntryIndex = { [name in Cate ...