Retrieving the keys from an object that has already been entered and converting them into types in TypeScript

const obj1 = {
    foo: 'bar',
    goo: 'car',
    hoo: 'dar',
    ioo: 'far'
} as const

const obj2 = {
    koo: 'gar',
    loo: 'har',
    moo: 'jar',
    noo: 'kar'
} as const

type KeysOfObj1 = keyof typeof obj1  // "foo" | "goo" | "hoo" | "ioo"
type ValuesOfObj1 = typeof obj1[KeysOfObj1]  // "bar" | "car" | "dar" | "far"
type KeysOfObj2 = keyof typeof obj2  // "koo" | "loo" | "moo" | "noo"
type ValuesOfObj2 = typeof obj2[KeysOfObj2]  // "gar" | "har" | "jar" | "kar"

type OtherObj = Record<string, ValuesOfObj1 | ValuesOfObj2>

const obj3: OtherObj = {
    hello: obj1.foo,
    world: obj2.koo
} as const

const obj4: OtherObj = {
    hi: obj1.hoo,
    anotherWorld: obj2.moo
} as const

type KeysOfObj3 = keyof typeof obj3  // string
type KeysOfObj4 = keyof typeof obj4  // string

I believe the code is self-explanatory. My query pertains to obtaining keys of obj3 and obj4 as types in the format shown below, instead of just a generic "string" type:

type KeysOfObj3 = keyof typeof obj3  // "hello", "world"
type KeysOfObj4 = keyof typeof obj4  // "hi", "anotherWorld"

Please review the comments provided as they may offer additional insights.

Answer №1

The reason behind this behavior lies within the line of code

type OtherObj = Record<string, ValuesOfObj1 | ValuesOfObj2>
, where the key is explicitly typed as string.

Therefore, when you use keyof typeof obj3, it references Record<string, ...> and applies that type.

If you wish to implement type checking, you will need to either utilize a type alias with generics or a generic function.

type OtherObj<T extends string> = Record<T, ValuesOfObj1 | ValuesOfObj2>

// Explicitly define keys
const obj3: OtherObj<'hello' | 'world'> = {
    hello: obj1.foo,
    world: obj2.koo
} as const

Alternatively, if you find the redundant verbosity undesirable, you have the option to slightly compromise runtime efficiency. This involves utilizing inference from type parameters to deduce the return value.

const createObj = <K extends string>(
  obj: Record<K, ValuesOfObj1 | ValuesOfObj2>
) => obj

const obj4 = createObj({
    hi: obj1.hoo,
    anotherWorld: obj2.moo,
    foo: 'foobar' //Error thrown! <-- this is an invalid value
} as const)

View this on TS Playground

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

Here is a way to retrieve the name of a ref object stored in an array using Vue.js 3 and Typescript

I have a Form, with various fields that I want to get the value of using v-model and assign them to ref objects. In order to populate my FormData object with this data, I require both the name and the value of the ref objects. Unfortunately, I am struggli ...

Is there a way to delegate properties in Angular 2+ similar to React?

When working with React, I have found it convenient to pass props down dynamically using the spread operator: function SomeComponent(props) { const {takeOutProp, ...restOfProps} = props; return <div {...restOfProps}/>; } Now, I am curious how I ...

I am eager to perform DOM manipulation similar to jQuery but within the context of Angular 6

Is there a way to modify the background color of the main div when a button is clicked? <div> <p>I'd like to be able to change the background color of the parent div by clicking a certain button. </p> <button (click)=" ...

requirements for navigating within an Angular application

Initially, if the first user is already logged in on the first tab and is redirected to the dashboard, when that same user opens a second tab in the same browser (by pasting the login URL), they are automatically redirected to the dashboard without having ...

Can default values be assigned to a DTO during initialization?

What is the method to assign default values when a query is empty? In the case where I have this DTO structure for a query: export class MyQuery { readonly myQueryItem: string; } If the request doesn't include any query, then myQuery.myQueryItem ...

Error in TypeScript: Unable to assign type 'string' to type 'number | ""'

I am new to TypeScript and struggling to comprehend the error message below. Type '{ order: string; }[]' is not compatible with type 'TestType[]'. Type '{ order: string; }' is not assignable to type 'TestType'. ...

Tips for effectively utilizing generics in typescript

Having trouble understanding how to properly utilize generics. Can someone help me use generics in the following scenario: export interface Location { id: number; address: { houseNumber: string; }; } export const getEuropeLocations = async ( ap ...

Here's a guide on accessing information from a local JSON file and displaying it on an HTML page using Ionic 2 with TypeScript

I have received a JSON file formatted like the following: { "records": { "patients": { "day": "Today", "details": [ { "name":"Diab", "stat":"Pending", "phno":"8197246465", "patNames":"Sandr ...

Ensuring Two Members in a Class are of Matching Types

I need to verify that two members of my class are of the same type, but I do not know what type they are. Any suggestions? I have attempted the following approach, but it did not work: interface Foo { bar: Foo["baz"]; baz: Foo["bar"]; } ...

Nextjs and Typescript are giving me an error that says, "The property 'nextUrl' is not found on type 'IncomingMessage'." What could be causing this

I'm currently utilizing the Next.js API route and attempting to retrieve the request parameters: export const GET = async (req: IncomingMessage) => { const matchString: String = req.nextUrl.searchParams.get("data") } I assume the typ ...

What is the best way to assign unique IDs to automatically generated buttons in Angular?

Displayed below is a snippet of source code from an IONIC page dedicated to shapes and their information. Each shape on the page has two buttons associated with it: shape-properties-button and material-information-button. Is it possible to assign different ...

Input tag paired with a date picker

Instead of using the <label>{{list.createdat | date: 'dd.MM.yyyy'}}</label> tag, I want to change it to an input tag to create a simple datepicker: <input type="date"/> The issue is that the data in the <label> tag is pa ...

Turn off the warning message that says 'Type of property circularly references itself in mapped type' or find a solution to bypass it

Is there a way to disable this specific error throughout my entire project, or is there a workaround available? The error message states: "Type of property 'UID' circularly references itself in mapped type 'Partial'.ts(2615)" https:/ ...

Angular Fails to Identify Chart.js Plugin as an Options Attribute

Encountering issues with using the 'dragData' plugin in Chart.js 2.9.3 within an Angular environment: https://github.com/chrispahm/chartjs-plugin-dragdata After importing the plugin: chartjs-plugin-dragdata, I added dragdata to the options as sh ...

It is not necessary to specify a generic type on a Typescript class

When working with Typescript and default compiler options, there are strict rules in place regarding null values and uninitialized class attributes in constructors. However, with generics, it is possible to define a generic type for a class and create a ne ...

Change the format of the array from the initial array to the new array structure

I have a multi dimensional array that I need to convert into the array2 structure. I've tried various methods, but all of them have resulted in bulky code and lots of iteration. Is there an easier way to accomplish this task? I am new to Angular and ...

Creating custom functionality by redefining methods in Typescript

My current scenario is as follows: abstract class A implements OnInit{ ngOnInit() { this.method(); } private method() { // carrying out tasks } } class B extends class A implements OnInit { ngOnInit() { thi ...

Determining the data type of an object property using a variable

I just joined a pre-existing project, where there are some global variables set by the backend. These globals are defined in an interface: interface IGlobals { feature_foo: boolean feature_bar: boolean someOtherProp: string } const globals: IG ...

Animation of lava effect in Angular 7

I have a unique Angular 7 application featuring a homepage with a prominent colored block at the top, along with a header and various images. My goal is to incorporate lava effect animations into the background similar to this CodePen example. If the link ...

Exploring the world of Vue and Pinia: managing and accessing data like

While delving into Vue and Pinia, I encountered a data management issue on the user side. On my main page, I showcase categories and auction items. However, upon navigating to a specific category in the catalog, the data for auction items remains in the st ...