Retrieve the keys of a type by analyzing one of its values

My global type definition consists of an object with keys (referred to as a prop) that must be passed by the user as a parameter to myFunction(), and values that are used solely for type checking within my code.

These values can fall into only 3 "possible types" (contained in Possible). Each possible type has its own distinct definition.

I want myFunction() to only accept props whose value's test property is equal to "possible1".

The generic type OnlyPossible1Props<T> demonstrates how I envisioned implementing this, but unfortunately it does not function as intended.

Initially, I encounter an error:

Type 'prop' cannot be used to index type 'T'. (2536)

In addition, instead of returning "abc" | "def" as expected in myFunction(), it returns string | number.

The closest workaround I've managed so far is the section of code that I have commented out. However, along the way, it loses key information because it isn't stored anywhere.

For easier debugging, you can access the type Possible = "possible1" | "possible2" | "possible3"; type ValueBase<T extends Possible, U, V> = { test: T, other1: U, other2: V }; type Value1<T> = ValueBase<"possible1", T, T>; type Value2<T> = ValueBase<"possible2", T, [T, T]>; type Value3<T> = ValueBase<"possible3", T, T[]>; type Definition = { [prop: string]: Value1<any> | Value2<any> | Value3<any> }; interface Example extends Definition { "abc": { test: "possible1", other1: string other2: string }, "def": { test: "possible1", other1: string other2: string }, "ghi": { test: "possible2", other1: string other2: [string, string] }, "jkl": { test: "possible3", other1: string other2: string[] } } // type OnlyPossible1Props<T> = T extends object ? // T[keyof T] extends infer A ? // A extends { test: "possible1" } // ? A : never : never : never; type OnlyPossible1Props<T> = T extends object ? keyof T extends infer prop ? T[prop] extends infer A ? A extends { test: "possible1" } ? prop : never : never : never : never; function myFunction<Prop extends OnlyPossible1Props<Example>>(prop: Prop) { }

Answer №1

When you remove extends Definition from Example, the following code works:

type OnlyPossible1Props<T> = {
    [K in
        { [J in keyof T]: T[J] extends { test: "possible1" } ? J : never }[keyof T]
    ]?: T[K]
};

This results in:

type ExampleOnlyPossible1Props = OnlyPossible1Props<Example>;

// equivalent to:

type ExampleOnlyPossible1Props = {
    abc?: {
        test: "possible1";
        other1: string;
        other2: string;
    };
    def?: {
        test: "possible1";
        other1: string;
        other2: string;
    };
}

(You may want to skip the optional modifier ?).

This gives us:

myFunction({ "abc": { test: "possible1", other1: "foo", other2: "bar" } }) // OK
myFunction({ "def": { test: "possible2", other1: ["a", "b"], other2: "bcd" } }) // Error

By using this approach, we can see how it works on the Example, you can use this alternative method:

type OnlyPossible1Props<T> = {
    [J in keyof T]: T[J] extends { test: "possible1" } ? T[J] : never
}[keyof T];

This will give you a union type that is demonstrated in this

The following works, but only if you remove extends Definition from Example:

type OnlyPossible1Props<T> = {
    [K in
        { [J in keyof T]: T[J] extends { test: "possible1" } ? J : never }[keyof T]
    ]?: T[K]
};

This results in:

type ExampleOnlyPossible1Props = OnlyPossible1Props<Example>;

// equivalent to:

type ExampleOnlyPossible1Props = {
    abc?: {
        test: "possible1";
        other1: string;
        other2: string;
    };
    def?: {
        test: "possible1";
        other1: string;
        other2: string;
    };
}

(You may want to skip the optional modifier ?).

This gives us:

myFunction({ "abc": { test: "possible1", other1: "foo", other2: "bar" } }) // OK
myFunction({ "def": { test: "possible2", other1: ["a", "b"], other2: "bcd" } }) // Error

You can see that in this TypeScript Playground.

The reason this does not work if Example extends Definition is that the index signature on Definition means keyof Example evaluates to string | number, rather than the actual keys of Example. If extends Definition is removed, we get keyof Example as "abc" | "def" | "ghi" | "jkl".

If you want myFunction to accept one of the possible1 props from Example, you can instead use:

type OnlyPossible1Props<T> = {
    [J in keyof T]: T[J] extends { test: "possible1" } ? T[J] : never
}[keyof T];

This gives you a union type that you can see that in this TypeScript Playground:

myFunction({ test: "possible1", other1: "foo", other2: "bar" }) // OK
myFunction({ test: "possible2", other1: ["a", "b"], other2: "bcd" }) // Error

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

Encountered an error while trying to install @material-ui/core through npm: Received an unexpected end of JSON input

npm install @material-ui/core npm ERR! Unexpected end of JSON input while parsing near '...X1F+dSMvv9bUwJSg+lOUX' npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\WR-022\AppData\Roaming\npm-cach ...

Alert: The route "/D:/original/22-02-2017/job2.html" specified in [react-router] does not correspond to any existing routes

I am currently working on a project using TypeScript with React. I'm utilizing webpack as a compiler, and my directory structure looks like this: d:/original/22-02-2017/ - In my web.config.js file, the entry point is set to ./src/index.tsx. All ...

What is the process for obtaining a flattened tuple type from a tuple comprised of nested tuples?

Suppose I have a tuple comprised of additional tuples: type Data = [[3,5,7], [4,9], [0,1,10,9]]; I am looking to develop a utility type called Merge<T> in such a way that Merge<Data> outputs: type MergedData = Merge<Data>; // type Merged ...

Trigger a React event using D3.js dispatch

While working with my axis, I encountered an issue when trying to dispatch a React event along with a payload. What I noticed was that when I used console.log('item'), a pointer event was being logged instead of the expected item property. Is thi ...

Troubleshooting MeshStandardMaterial problem in Three.js on Mac M1 with Chrome

Having an issue on my M1 Mac with Chrome, where my scene appears like https://i.sstatic.net/tWckT.png. However, it looks fine in Safari or Firefox https://i.sstatic.net/9TJvQ.png The problem seems to be related to the rendering of walls. Here is my code: ...

Utilizing React with .NET 8.0 and Minimal API, the front end is configured to send HTTP requests to its own specific port rather than the

Transitioning from working with react projects on .NET 3.1 to creating a new project on .NET 8.0 has been a challenging and confusing experience for me. When I attempted to access a newly created controller method, I encountered the error 404 Not Found. It ...

Encountering an undefined property error while using Array.filter in Angular 2

hello everyone, I am currently faced with an issue while working on a project that involves filtering JSON data. When using the developer tools in Chrome, it keeps showing me an error related to undefined property. chart: JsonChart[] = []; charts: JsonC ...

Is there a way to retrieve a compilation of custom directives that have been implemented on the Vue 3 component?

Is there a way to retrieve the list of custom directives applied to a component? When using the getCurrentInstance method, the directives property is null for the current component. I was expecting to see 'highlight' listed. How can I access the ...

The Vue store array declaration triggers a TS error stating that it is not assignable to a parameter of type never

I'm puzzled as to why this error keeps showing up: Argument of type '{ id: string; }' is not assignable to parameter of type 'never'. ... appearing at const index = state.sections.findIndex((section) => section.id === id); T ...

Issues with Angular ng-bootstrap tabset component not functioning as expected

{ "name": "ModalWindow", "version": "1.0.0", "repository": { "type": "git", "url": "" }, "scripts": { "build": "webpack --mode production", "start": "webpack-dev-server --mode development --open" }, "license": "MIT", "depend ...

What are some strategies for distinguishing between callable and newable types?

I seem to be facing a challenge related to narrowing, specifically the differentiation between Fnc (callable) and Class (newable). The code snippet provided functions in Playground, but the autocomplete feature is lacking, as shown in the first image. My ...

The required property '0' is not found in the type 'any[]' but it is needed in the type '[{ post_id: string; title: string; }]'

I have gone through this post but I am still struggling to understand the issue. Why am I unable to pass this array to the update call? // create a new object with updated post properties const newPost = await this.postRepository.create(data); await thi ...

Angular 6 Calendar Template issues with parsing: Unable to link to 'view' as it is not recognized as a valid property of 'div'

I am in the process of developing an application that utilizes this angular calendar. My tech stack includes Angular 6 with AngularFire2 and Firebase. Below is my app.module.ts file: import { BrowserModule } from '@angular/platform-browser'; imp ...

What is the best way to mandate the declaration or type of a function in TypeScript?

Let me present my dilemma: I am aiming to create a declaration file containing TypeScript function models. This file will be utilized by various individuals to build their own programs. To achieve this, I have crafted a def.d.ts file with a small example ...

Errors caused by Typescript transpilation only manifest on the production server

During the process of updating my node version and dependencies on both machines, I came across an issue where building my app in production on one machine resulted in an error, while building it on my main machine did not. I found that the errors disappe ...

Determine in Typescript if a value is a string or not

In my code, I have a component: export type InputData = string | number | null; interface InputData { data?: string | number | null; validate: boolean; } const App: React.FC<InputData> = ({ data = '', validate = true, }) => ...

Displaying Information in Angular Modal Windows

I'm facing an issue while trying to create an edit button for a formGroup that is initially saved. When the user clicks on the adjacent button, a modal should open with editable data. However, I encountered this error and haven't been able to res ...

What could be causing my D3.js stacked bar chart to show inaccurate results?

I'm encountering some challenges while creating a stacked bar chart in d3.js. The current appearance of my chart is depicted here (developed with D3.js): https://i.sstatic.net/G6UA6.png However, I aim to achieve a design similar to this one (crafted ...

A practical guide to effectively mocking named exports in Jest using Typescript

Attempting to Jest mock the UserService. Here is a snippet of the service: // UserService.ts export const create = async body => { ... save data to database ... } export const getById = async id => { ... retrieve user from database ... } The ...

The error message in React JS Typescript states: "Cannot assign a string to a parameter that requires a SetStateAction<number>"

Hey there! I encountered this error: Argument of type 'string' is not assignable to parameter of type 'SetStateAction<number>'. Here is a snippet of my code where the error occurred: . . . const[ idPadre, setIdPadre ] = useState& ...