Find the combined key names in an object where the values can be accessed by index

I am currently working on creating a function called indexByProp, which will only allow the selection of props to index by if they are strings, numbers, or symbols.

This particular issue is related to https://github.com/microsoft/TypeScript/issues/33521. You can find my current attempt at building this function in this TS Playground link.

The expected result I anticipate should look like:

indexByProp('bar', [{ // should be ok
  bar: 1, 
  foo: '2', 
  qux: () => {}
}])

indexByProp('qux', [{ // should be type error
  bar: 1, 
  foo: '2', 
  qux: () => {}
}])

Answer №1

If you're seeking a solution like the one below:

type MatchingKeys<T, V> = NonNullable<
  { [K in keyof T]: T[K] extends V ? K : never }[keyof T]
>;

where MatchingKeys<T, V> provides the keys of T with properties that can be assigned to V. This involves looking up property values from a mapped type based on conditional types. Here's an example to demonstrate how it operates:

interface Bar {
  x?: string;
  y: number;
}

// Fetching all keys of Bar where properties are assignable to 
// string | undefined... expecting "x" as output
type ExampleCase = MatchingKeys<Bar, string | undefined>;

This is executed as follows:

type Result1 = NonNullable<
  {
    x?: Bar["x"] extends string | undefined ? "x" : never;
    y: Bar["y"] extends string | undefined ? "y" : never;
  }["x" | "y"]
>;

type Result2 = NonNullable<
  {
    x?: string | undefined extends string | undefined ? "x" : never;
    y: number extends string ? "y" : never;
  }["x" | "y"]
>;

type Result3 = NonNullable<{ x?: "x"; y: never }["x" | "y"]>;

type Result4 = NonNullable<"x" | undefined | never>;

type Result5 = NonNullable<"x" | undefined>;

type Result6 = "x";

The expected result is "x".


Moreover, IndexableKeys can be defined simply as:

type IndexableKeys<T> = MatchingKeys<T, keyof any>;

Your indexByProp() function would then appear as shown below:

const indexByProp = <X extends Indexable>(
  keyName: IndexableKeys<X>,
  items: X[]
): Indexable<X> => {
  const initial: Indexable<X> = {};
  return items.reduce((index, item) => {
    const keyValue = item[keyName];
    index[keyValue as keyof typeof index] = item; // assertion needed here
    return index;
  }, initial);
};

Your test cases should work correctly:

indexByProp("value", [
  {
    value: 1,
    label: "2",
    action: () => {}
  }
]); // this should pass

indexByProp("action", [
  //        ~~~~~  error!
  // Argument of type '"action"' is not assignable to parameter of type '"value" | "label"'.
  {
    // this should show an error
    value: 1,
    label: "2",
    action: () => {}
  }
]);

Hopefully, this explanation will assist you. Best of luck!

Click for code sample

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

Utilize cypress to analyze the loading time of a webpage

My current challenge involves using cypress to carry out website tests. I am looking for a reliable method to measure the duration it takes for certain cypress commands to load or execute. As an example: //var startTime = SomeStopwatchFunction(); cy.visit( ...

Retrieve the TaskID of an ECS Fargate container for exporting and future use within AWS CDK code

Currently, I am leveraging CDK version 2 alongside Typescript. In my current setup, I encounter a situation where I necessitate the TaskID value from ECS Fargate Container to be incorporated into another command. The process involves me utilizing new ecs ...

Is it possible to modify the parameters of a function by utilizing a MethodDecorator without affecting the "this" value?

Consider a scenario where you need to dynamically modify method arguments using a decorator at runtime. To illustrate this concept, let's simplify it with an example: setting all arguments to "Hello World": export const SillyArguments = (): MethodDec ...

Angular 2 Mixup: Using Leaflet and Google Maps with Incorrect Tile Order

I am encountering an issue while working on Leaflet and Google within Angular 2. The problem lies in the Tilemill tiles not rendering properly as they are displaying in a strange order. Here is a screenshot of the current state: https://i.stack.imgur.com/ ...

A Typescript object that matches types and eventually returns a string when called upon

In the process of overengineering a type that can match either a string or an object whose valueOf() function, when evaluated recursively, ultimately returns a string. type Stringable = string | StringableObject; interface StringableObject { valueOf() ...

React: Resolving the issue of "children" property not found in type "IntrinsicAttributes & Props"

Currently, I am attempting to retrieve data from an API and showcase it in a list of cards using React with TypeScript. As I am relatively new to working with React in Typescript, I am uncertain about how to resolve this error or if I have overlooked somet ...

What is the best way to implement multiple templates for a single component?

Is there a way to configure my Home Component based on the user's role? For instance: If the employee is an admin, then the home component should load the template URL designed for admins. Likewise, if the employee is a cashier, then the home compo ...

Problem with file organizer in Angular application - unable to see files and directories

I successfully integrated a file manager component following this tutorial. Despite no errors in vscode or chrome debug tool, my folders are not visible. Can anyone help me troubleshoot this issue? https://i.stack.imgur.com/ihEak.png I am unsure how to r ...

The upcoming development does not involve creating an entire HTML webpage using on-demand static site generation (SS

I’m encountering a problem when utilizing getStaticPaths and getStaticProps to create an on-demand SSG for a sharing page. My setup involves Next v12.1.0 and React 17.0.2. After building a specific /[id] page, I can retrieve the data but the HTML output ...

The autocomplete feature in Atom is not functioning as expected

Autocomplete+ is included with the installation of Atom and is activated by default. I have noticed that when I am coding, no suggestions are appearing. What could be causing this issue? Do I need to adjust any files in order for Autocomplete+ to functio ...

The specific property 'splice' cannot be found within type 'T'

As I delve into working with TypeScript, an unexpected error arises: Property 'splice' does not exist on type 'T'. type Item = { name: string, body: string, imgOne: string, imgTwo: string, }[] // Another file contains this func ...

Declaring a sophisticated array as a property within another property in Typescript

As a newcomer to Angular and Typescript, I am facing a challenge while declaring a property with a complex array as one of its values. Here is what I have attempted: groupedItem: { customGroupId: string, cgName: string, category: [{ cu ...

The React table column definition inexplicably transforms into a string

When working with react-table in Typescript, I encountered an issue while defining the type for my columns within a custom hook. It seems that when importing the hook and passing the columns to my Table component, they are being interpreted as strings inst ...

Navigating forwards in Angular 7 causes loss of state

I have a situation with my angular 7 Ionic application. When I navigate to a page, I use the following code snippet: navigateToDetail(entity: User) { const navigationExtras: NavigationExtras = { state: { entity, entityId: entity.id ...

Displaying properties of a class in Typescript using a default getter: Simplified guide

Here is an interface and a class that I am working with: export interface ISample { propA: string; propB: string; } export class Sample { private props = {} as ISample; public get propA(): string { return this.props.propA; } public se ...

Dealing with server-side errors while utilizing react-query and formik

This login page utilizes formik and I am encountering some issues: const handleLogin = () => { const login = useLoginMutation(); return ( <div> <Formik initialValues={{ email: "", password: "" }} ...

Utilizing a Typescript class interface does not maintain the original method types

Struggling to define a Typescript interface and implement it in a class. The issue lies in the method signatures of the interface not being applied to the class as expected. Below is a simplified example: export interface Foo { bar(value: string): voi ...

The absence of a 'defaultValue' argument in Typescript React is causing an issue

I'm encountering an issue with my code. The error message states "An argument for 'defaultValue' was not provided." I am trying to set default values but unsure of what those values should be. Additionally, I am facing a couple of other err ...

Is it possible to execute TestCafe tests using TypeScript page objects that have not been utilized?

While working with TestCafe, I am implementing tests using the Page Objects pattern. I have already written some page objects in advance, even before their actual usage, as I am familiar with the page and know what to anticipate. However, when attempting ...

What is the best way to reduce the size of a Base64/Binary image in Angular6

I utilized the Ngx-Webcam tool to capture images from my camera. My goal is to obtain both high quality and low quality images from the camera. Although this library provides me with Base64 images, it offers an option to reduce the size using imageQuality ...