Using TypeScript to create a list of object keys defined as [key: string] key/value pairs

I'm looking to define an interface in TypeScript, but I'm unsure of how to go about it. Here's what I have in mind:

export interface SomeInterface {
  testProp:{
  [key: string]: {
    prop1: string;
    prop2?: string;
    prop3?: string;
    ....

  };
}
  requiredProps: ???? // <- here i would like to define type to be array of used keys
}

For example, consider this object:

 const value = {
    testProp: {
      orange: {
        prop1: 'test1',
      },
      kiwi: {
        prop1: 'random text',
      },
      lemon: {
        prop1: 'text',
      },
    },
   requiredProps: [] // posible items in array "lemon", "kiwi", "orange"
  };

I attempted to define requiredProps as

requiredProps: [keyof Pick<SomeInterface,"testProp"]
but was unsuccessful.

Answer №1

There isn't a specific type that directly fits your requirements. However, you can represent it as a generic type. Here's an example:

export interface SomeCustomInterface<K extends PropertyKey, R extends K> {
    testProperty: Record<K, {
        prop1: string;
        prop2?: string;
        prop3?: string;
    }>;
    requiredProperties: R[]
}

This approach will restrict things the way you intend, but when creating a value of the SomeCustomInterface type, you must specify the K and R parameters. To have the compiler infer these, you can use a helper function:

const asCustomInterface = <K extends PropertyKey, R extends K>(x: SomeCustomInterface<K, R>) => x;

Usage example:

const customValue = asCustomInterface({
    testProperty: {
        apple: {
            prop1: 'example',
        },
        banana: {
            prop1: 'some text',
        },
        mango: {
            prop1: 'content',
        },
    },
    requiredProperties: ["apple", "banana"]
});

You'll encounter an error if you add an element to requiredProperties that is not a key in testProperty:

asCustomInterface({
    testProperty: { x: { prop1: "" }, y: { prop1: "" }, z: { prop1: "" } },
    requiredProperties: ["x", "y", "z", "w"] // error!
})

Making SomeCustomInterface<K, R> a generic type introduces complexity, as any values or functions dealing with them need to carry additional type parameters. You may want to reserve generics for scenarios where input from external users is uncertain, while using a non-generic version internally for easier handling:

// Code visible to external users, enforcing constraints
function publicFunction<K extends string, R extends K>(
  someCustomInterface: SomeCustomInterface<K, R>
) {
    internalFunction(someCustomInterface)
}

// Internal code, working with non-generic version
type SimplifiedCustomInterface = SomeCustomInterface<string, string>
const simplifiedValue: SimplifiedCustomInterface = customValue; // accepted
function internalFunction(simplifiedCustomInterface: SimplifiedCustomInterface) {
    // Perform operations
}

Click here to view the Playground link

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

`Mapping child routes in Angular using basic components`

I am encountering an issue with Angular 8 routes. The problem lies in the child routes not functioning properly. Here are my defined routes: const routes: Routes = [ { path: 'admin', component: MainComponent, children: [ { path: &apo ...

the value of properrty becomes undefined upon loading

In my code, there exists an abstract class named DynamicGridClass, containing an optional property called showGlobalActions?: boolean?. This class serves as the blueprint for another component called MatDynamicGridComponent, which is a child component. Ins ...

TypeScript encounters a problem when attempting to concatenate arrays with different signature overloads

I encountered the following error message: There is an issue with overloading. Overload 1 of 2, '(...items: ConcatArray<{ title: string; width: number; dataIndex: string; render: (level: string) => Element; }>[]): { title: string; width: nu ...

Link the Angular Material Table to a basic array

Currently facing a challenge with the Angular Material table implementation. Struggling to comprehend everything... I am looking to link my AngularApp with a robot that sends me information in a "datas" array. To display my array, I utilized the following ...

What is the best way to execute a function that retrieves data from a MySQL query and then sends it back as a result in Express.js?

How can I refactor my code to efficiently call a function that returns the result of a MySQL query and send it back in an Express.js response? I am attempting to streamline my SQL queries by exporting them into individual functions to eliminate duplicatio ...

Running a TypeScript file on Heroku Scheduler - A step-by-step guide

I have set up a TypeScript server on Heroku and am attempting to schedule a recurring job to run hourly. The application itself functions smoothly and serves all the necessary data, but I encounter failures when trying to execute a job using "Heroku Schedu ...

There is a potential for the object to be 'undefined' when calling the getItem method on the window's local storage

if (window?.sessionStorage?.getItem('accessToken')?.length > 0) { this.navigateToApplication(); } Encountering the following error: Object is possibly 'undefined'.ts(2532) Any suggestions on how to resolve this issue? I am attem ...

What is the best way to create a TypeScript function that merges actions together?

I am currently working on a TypeScript function similar to the following: import multipleActionObject from page; it("should be able to perform multiple operations", () => { multipleActionObject.chooseIndex(4).setValue(10); } Inste ...

Typescript: Potential null object error when defining a method

I recently encountered an error message stating "Object is possibly null" while working on the changePageSize method in book-store.component.html. It seems like I need to initialize the object within the class, but I'm not familiar with how to do that ...

What is the reason behind the narrowing of the type by indexing into a mapped type?

This question is inspired by an amazing answer found here: My curiosity lies in why the indexing works in the mapped type trick. Let's illustrate with an example: type MyData = { a: { alpha: string; }; b: { beta: number; } } type ...

What is the best way to define a function agreement in Typescript?

I have created a function that can return `undefined` only when its argument is also `undefined`, otherwise it will always return a value derived from the argument provided. Here's an example of how the function works: function triple(value?: number) ...

Experimenting with Vuejs by testing a function that delivers a Promise upon the execution of the "Created" hook

In my Vuejs application, I have the following script written in Typescript: import { Foo, FooRepository } from "./foo"; import Vue from 'vue'; import Component from 'vue-class-component'; import { Promise } from "bluebird"; @Component ...

Can we handle optional properties that are contingent on a boolean in the type?

My current scenario involves a server response containing a boolean indicating success and optional data or error information. type ServerResponse = { success: boolean; data?: { [key: string]: string }; err?: { code: number, message: string }; } Dea ...

How can I dynamically update content using <router-outlet> on a secondary page?

When accessing my homepage, I want to see a header, footer, and the home-news-page displayed. Additionally, when I click on a link in the header, I would like the content of the home-news-page to change accordingly. Here is how my routing is currently set ...

What is causing Angular to consistently display the first object in the array on the child view, while the child .ts file correctly prints information from a different object?

Once a card of any object is clicked, the information of that specific object will be printed to the console. However, the child view will only display the details of the first object in the array it retrieves from. All pages are included below. A visual e ...

Trouble with Displaying Events on React Big Calendar with Typescript

Struggling to implement React Big Calendar with TypeScript. Managed to get the calendar to display correctly after adjusting the height, but unable to show any events. The array of events is populating as expected, and I modified the code for TypeScript co ...

NPM Package: Accessing resources from within the package

My Current Setup I have recently developed and published an npm package in typescript. This package contains references to font files located within a folder named public/fonts internally. Now, I am in the process of installing this package into an Angul ...

How to Extract the Specific Parameter Type from a Function in Typescript

After generating a client for an API using typescript-node, I encountered the following code: export declare class Api { getUser(username: string, email: string, idType: '1298' | '2309' | '7801') } I need to access the ...

How can I configure Angular to redirect to an error page whenever an error is encountered in an HTTP API request?

With over 50 HTTP APIs in my application, I'm looking for a way to automatically route to a specific page whenever an error message or issue arises with one of the APIs. Instead of manually adding routing to each service, I want to find a simpler and ...

One approach to enhance a function in Typescript involves encapsulating it within another function, while preserving

What I Desire? I aim to create a function called wrap() that will have the following functionality: const func = (x: string) => 'some string'; interface CustomObject { id: number; title: string; } const wrapped = wrap<CustomObject> ...