Specify markers to suggest a literal object type that is not explicitly stated

Currently, I am developing a function that takes in a configuration parameter, which is essentially an object with a highly variable structure. Depending on the type of configuration provided, the function should output something of equally diverse structure. My aim is to maximize TypeScript inferrence for detecting the config type while also allowing users to hint at certain parts of it.

The following snippet accomplishes my objective:

type Type<T> = (_: T) => T;
const type =<T>(): Type<T> => T => T;

type ListConfig<T> = {
    itemType: Type<T>;
}

type Config = {
    [name: string]: ListConfig<any>; 
}

type ItemType<L> = L extends ListConfig<infer T> ? T : never;

const createLists = <C extends Config>(
    cfg: C
): { [K in keyof C]: Array<ItemType<C[K]>> } => {
    return {} as any;
}

const config = {
    numbers: {
        itemType: type<number>(),
    },
    strings: {
        itemType: type<string>(),
    }
};

// The result correctly inferred as { numbers: number[], strings: string[] }
const lists = createLists(config);

Playground Link here

In this revised code snippet, I still want TypeScript to infer the type of config[K] (aside from the item type). For this to work, partial type argument inferrence would be necessary.

EDIT: In my project, I am developing a library to simplify the creation of Redux store/actions. Essentially, I plan to provide State<T> and createRedux, which will allow me to achieve the following:

const myConfig = {
  $combine: {
    listOfNumbers: {
      $preset: 'list' as 'list',
      $item: {
        $type: {
          $itemType: type<string>(),
        }
      },
    }
  }
};

type MyState = State<typeof myConfig>;
// MyState == { listOfNumbers: string[] }
const { Actions } = createRedux(myConfig);
// typeof Actions == {
//   listOfNumbers: {
//     push: (item: string) => ReduxAction
//   }
// }

Answer №1

It's important to consider how you want to proceed in this situation since there may not be much runtime impact based on the types being used. If you have initial array contents or a type guard function, there could be more concrete suggestions available.


One potential approach is to create a mapping from type names to types and mandate that the config utilizes these names:

interface TypeNames {
  string: string,
  number: number,
  boolean: boolean,
  arrayOfStrings: string[],
  // etc
}

type ListConfig<K extends keyof TypeNames> = {
  itemType: K
}

type Config = {
  [name: string]: ListConfig<keyof TypeNames>;
}

const createLists = <C extends Config>(
  cfg: C
): { [K in keyof C]: Array<TypeNames[C[K]['itemType']]> } => {
  return {} as any;
}

// The inferred result will correctly be { numbers: number[], strings: string[] }
const lists = createLists({
  numbers: { itemType: "number", otherStuff: "foo" },
  strings: { itemType: "string", otherStuff: 1234 }
});

This method works to an extent but relies on a fixed (potentially expandable) list of types.


Another approach is to prompt the caller of createLists() to specify an object type for the type parameter, with keys aligning with the configuration keys and values corresponding to the relevant list types:

const createLists = <T>() => <C extends Record<keyof T, any>>(
  cfg: C
): { [K in keyof C]: K extends keyof T ? Array<T[K]> : any } => {
  return {} as any;
}

// This will correctly infer { numbers: number[], strings: string[] }
const lists = createLists<{ numbers: number, strings: string }>()({
  numbers: { otherStuff: "foo" },
  strings: { otherStuff: 1234 }  
});

(Notice the curried function given the absence of partial type inference.) While slightly redundant due to specifying keys twice, I find this method cleaner overall.


Hopefully, these ideas spark some inspiration. Best of luck!

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

Why is the selected option not visible in the Angular 8 drop-down?

This is a commonly asked question, but my situation seems to be unique. None of the existing answers have provided a solution for my specific issue. Here is the code that I am working with: <form (ngSubmit)="getExceptions(f)" #f="ngForm"> ...

Utilizing the Composition Root concept in a TypeScript Express application

I am trying to grasp the concept of implementing a composition root in a project. Based on my research, improper usage of the composition root (such as referencing it in multiple places within your application code) can lead to the service locator antipat ...

The specified file cannot be found within the Node file system

I am working on a project that involves reading a file using Node fs, and I have the following code: const files: FileSystemTree = { "Component.tsx": { file: { contents: fs.readFileSync( `../../apps/components ...

The object literal is restricted to defining existing properties, and 'id' is not a recognized property in the type 'FindOptionsWhere<Teacher>'

I encountered a persistent error within my teachers.service: Object literal may only specify known properties, and 'id' does not exist in type 'FindOptionsWhere | FindOptionsWhere[]'.ts(2353) FindOneOptions.d.ts(23, 5): The expected t ...

Recreating components with every change check, Angular 2's *ngFor feature constantly updates

Within my code, I am utilizing two nested *ngFor loops. The first loop iterates through libraries, while the second one iterates through all items within each library, where a specific Angular component is dedicated to each item. The issue arises when the ...

Submitting a POST request from a Typescript Angular 2 application to a C# MVC backend

Having trouble passing a payload using Typescript service in an http.post request Here is my TypeScript code: saveEdits(body: Object): Observable<Animal[]> { let bodyString = JSON.stringify(body); let headers = new Headers({ 'Content- ...

Exploring the capabilities of supertest by testing endpoints in Express with NodeJS

Having trouble setting up a test environment to test my TypeScript Express Node.js endpoints. Here are the packages I've installed: jest ts-jest jest-junit supertest @types/supertest @types/jest This is how my spec file looks like: imp ...

The immutability of the List in JavaScript and its

I am confused about how the size of an Immutable JS List grows. According to the official documentation example at https://facebook.github.io/immutable-js/docs/#/List/push, pushing something into an immutable js List should automatically increase its size ...

The issue arises when TypeScript declarations contain conflicting variables across multiple dependencies

My current project uses .NET Core and ReactJS. Recently, I updated some packages to incorporate a new component in a .tsx file. Specifically, the version of @material-ui/core was updated from "@material-ui/core": "^3.0.3" to "@material-ui/core": "^4.1.3" i ...

Passing Selected Table Row Model Data to Backend in Angular 7

My goal is to send the selected data in a table row, which I select through a checkbox, to the server. However, I'm unsure about how to handle this via a service call. While I have the basic structure in place, I need assistance with sending the items ...

Inheritance from WebElement in WebdriverIO: A Beginner's Guide

I am seeking a solution to extend the functionality of the WebElement object returned by webdriverio, without resorting to monkey-patching and with TypeScript type support for autocompletion. Is it possible to achieve this in any way? class CustomCheckb ...

TypeScript Implementation of ES6 Arrow Functions

Just diving into Typescript, I'm struggling to figure out the solution. I tried researching and looked into destructuring, but still unable to make it work. import React from "react"; import { StyleSheet, Text, View } from "react-native"; const st ...

Determine the type of the final function within a variable number of nested closures

Imagine you have a function like this: const f = a => b => ... x => { return somevalue } Is there a way to determine the type of just the final function typeof x => { return somevalue } even if we don't know how many closures come before ...

What is the best way to customize a React component using TypeScript?

Let's say I have a functional component like this: function MyComp({a, b, c, d, e, f}: any) { ... } Is there a way to create a specialized version of this component where I provide values for "a" and "b," but allow other users to choose the r ...

Does the TS keyof typeof <Object> rule prohibit the assignment of object.keys(<Object>)?

I'm having trouble understanding the issue with this code snippet. Here is the piece of code in question: export type SportsTypes = keyof typeof SportsIcons export const sports: SportsTypes[] = Object.keys(SportsIcons); The problem arises when I at ...

Filtering an array dynamically in Typescript depending on the entered value

My task involves filtering arrays of objects based on input field values. Data data: [{ taskname: 'Test1', taskId: '1', status: 'Submitted' }, { taskname: 'Test2', taskId: '2', status: 'Re ...

Having difficulty resolving all parameters for the component: (?, [object Object]) in the Jasmine component Unit Test

While defining a UT for a component with an extended class using i8nService and ChangeDetectionRef, I encountered an error preventing me from instantiating it: Failed: Can't resolve all parameters for BrandingMultiselectComponent: (?, [object Object] ...

Is there a way to create identical copies of data with the identical names?

Here is the code I have written: this.temp1.push(this.json); for(let i=0; i<this.temp1.length; i++){ if(this.temp1[i].name == this.json.name){ this.orderList[i] = this.json; this.DBorder[i] = this.order_json; } ...

Error: While working in an Angular project, a TypeError occurs because the property '****' cannot be read when used within a forEach loop

As I attempt to iterate over this.data.members and perform certain actions within the forEach loop on this.addedUsers, I encounter a TypeError: Cannot read property 'addedUsers' of undefined. Interestingly, I can access this.data.members outside ...

A guide on implementing isomorphic types in TypeScript

Consider the following scenario with two files, file.ts: let a: Message = "foo" let b: Message = "bar" let c: Message = "baz" Now let's introduce a second file, file2.ts type Message = string function fun(abc: Message): void { } When using functi ...