What is the best way to retrieve the names of "string" properties from an interface in TypeScript?

Explore Typescript playground

I aim to extract the string characteristics from SOME_OBJECT and form them into a union type. Thus, I anticipate STRING_KEYS to signify

"title" | "label"

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractString<T> = keyof T extends string ? keyof T : never;

type STRING_KEYS = ExtractString<SOME_OBJECT>  // <----- INTENDED TO BE "title" | "label"

This is the output I'm currently obtaining:

https://i.sstatic.net/PVuG5.png

I feel like I'm making progress in this direction (using conditional types), but there's still room for improvement. How can I accomplish this task most effectively?

Answer №1

After finding inspiration in the detailed explanation provided in Typescript Docs on Distributive Conditional Types, I successfully adapted and implemented the concept:

https://i.sstatic.net/P96oO.png

The following code snippet showcases my adaptation, which effectively accomplishes the intended functionality:

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractStringPropertyNames<T> = {
    [K in keyof T]: T[K] extends string ? K : never
}[keyof T]

type STRING_KEYS = ExtractStringPropertyNames<SOME_OBJECT>

https://i.sstatic.net/NF0Tf.png

Explore this concept in Typescript playground

If there are alternative methods or more straightforward approaches to achieving the same outcome, I am eager to learn and explore them further. The current implementation might seem like a workaround, as it lacks clarity in explaining its purpose.


UPDATE (INSIGHT INTO THE PROCESS)

Upon deeper examination to comprehend the underlying functionality of the code, I have managed to break it down into two distinct steps:

STEP 1

Initially, a new object/type is constructed based on the keys from the generic type T (SOME_OBJECT in this instance).

For each property key K, the corresponding value is evaluated to determine if it extends the type string. If so, the key name is retained as the value; otherwise, it is set to never. The result can be seen in STEP_1_RESULT.

https://i.sstatic.net/gk7jr.png

STEP 2

In this phase, we utilize the object generated from step 1 to retrieve all possible values by defining

type STEP_2<T> = T[keyof T]
.

Since keyof T represents the union of all properties within T, Typescript returns a union containing all potential values for the STEP_1_RESULT object when called with members of the keyof T union.

Ultimately, redundant never types are eliminated from the union, leaving us with

"title" | "label"
, as demonstrated in our example.

https://i.sstatic.net/Dj0II.png

Answer №2

I'm still wrapping my head around conditional types, but one approach I might consider is utilizing the Pick utility type. It would look something like this:

interface SOME_OBJECT {
    title:      string,
    label:      string,
    someBool:   boolean,
    someDate:   Date,
    someNumber: number
}

type ExtractString = Pick<SOME_OBJECT, "title" | "label">;

type STRING_KEYS = ExtractString

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

"Loop through an array using forEach leads to a subscription that

I am a beginner in Angular and struggling to understand how async functions work. I have written the following code, but I am encountering an error: GET https://localhost:44353/api/ecams/id/undefined 400 and ["The value 'undefined' is not va ...

Transferring information between Puppeteer and a Vue JS Component

When my app's data flow starts with a backend API request that triggers a Vue component using puppeteer, is there a way to transfer that data from Backend (express) to the vue component without requiring the Vue component to make an additional backend ...

Tips on maintaining the chosen product while navigating to a different component

I have a dilemma with 2 different components that are responsible for creating an invoice. The first component adds more products The second component adds invoice details Initially, I enter the invoice details and select the client's name. The sel ...

Tips for sending user IDs through an Angular 8 interceptor

Alright, In my Angular application that is using version 8, I have an HttpMaintenanceInterceptor configured without the use of cookies. Instead, I have a getAccessToken method within the authService as shown below: getAccessToken(): string { return ...

Using JSON as a variable solely for determining its type and guaranteeing that the import is eliminated during compilation

In my TypeScript backend project with Node as the target runtime, I have a JSON file that is auto-generated within my repository. I use the following code to import the JSON file in order to get the type of the JSON object: import countries from '../g ...

Array updating using the foreach method in Angular

Hey everyone, I've encountered an error that seems to be related to scope and I could use some advice. I'm currently looping through an array and trying to push the results to another array. However, when I attempt to push the results to public m ...

Tips for implementing <mat-progress-bar> in .ts file when making API service requests with Angular

I'm currently utilizing an API call to retrieve an image from a service, and I would like to display a progress bar while the image is being fetched. It seems that I need to incorporate the progress bar within the service as the image data is returned ...

What is the role of @Output and EventEmitter in Ionic development?

I'm currently working on integrating Google Maps and Firebase database. My goal is to save my location in the Firebase database and transfer data using @Output and eventEmitter. However, I am facing an issue where pickedLocation has a value but this.l ...

Typescript support on Emacs

"Is there a way to enable Typescript syntax highlighting in Emacs?" I have been struggling with this for quite some time. Using Emacs 24 on an Ubuntu Virtualbox VM, I can't seem to get package-refresh-contents to work as it just hangs on "Contacting ...

React Native: Issue with the data section in FlatList

I encountered an issue while using Flatlist to address a problem, but I ran into an error with the data property of my Flatlist. The error message is not very clear and I'm having trouble understanding it ( No overload matches this call. Overload 1 of ...

Update all occurrences of a particular value to null within the Realtime Database using Firebase Cloud Functions

I need to update the values of a specific userID linked to multiple post keys in my database by setting the userID to null. The userIDs are associated with post keys located in the path: posts/ivies/userIDs in my database. Take a look at how the database i ...

Removing a method signature from a type that extends a function returning any in TypeScript

I am currently developing an API in typescript where I aim to return a function that includes multiple functions as properties, one of which is the same function. My approach to achieving this includes: type MainFunc = () => PublicInterface type Publi ...

Tips for accessing the value from a subscription within a function in Ionic 3

I am working on a function that retrieves a JSON file from a specific URL. The issue I am facing is that I am trying to access a random object from this data within the file, stored in this.data. However, when I attempt to console.log(this.data) outside of ...

Run the function solely once the asynchronous function has been executed

I need function F1() to wait for function F2() to fully execute and receive the response from a REST call in order to set some data. Here is the code I attempted to use: this.F1().subscribe(result => { this.F2(result); }) F1() { retur ...

Getting Session from Next-Auth in API Route: A Step-by-Step Guide

When printing my session from Next Auth in a component like this, I can easily see all of its data. const session = useSession(); // ... <p>{JSON.stringify(session)}</p> I am facing an issue where I need to access the content of the session i ...

Methods for verifying an empty array element in TypeScript

How can I determine if an element in an array is empty? Currently, it returns false, but I need to know if the element is blank. The array element may contain spaces. Code let TestNumber= 'DATA- - -' let arrStr =this.TestNumber.split(/[-]/) ...

Eliminate the usage of JSON.stringify in the Reducer function

I have a system where I store chat messages in a dictionary with the date as the key and a list of messages as the value. Whenever a new message is added, the following code snippet is executed. Is there a way to enhance the existing code to eliminate the ...

Issue: The function (0, react__WEBPACK_IMPORTED_MODULE_1__.useActionState) is not recognized as a valid function or its output is not iterable

I found a great example of using useActionState at this source. Currently, I am implementing it in my project with Next.js and TypeScript. app/page.tsx: "use client"; import { useActionState } from "react"; import { createUser } from ...

Utilize the ng2-select component to incorporate multiple instances within a single webpage

Can someone help me figure out how to use two ng2-select components in my modal? I've checked out the documentation, but it doesn't provide any information on using more than one select. I'm not sure how to capture the selected values of ea ...

The customer's status cannot be determined

I've encountered an issue with my TypeScript code that includes a simple if-else logic. endDate: String = ''; customerStatus: String; this.endDate = this.sampleData.customerStartDate; if (this.endDate == null) { this.customerStatus = ' ...