Is it possible to confirm that a value is a valid key without prior knowledge of the object's keys during compile-time?

Is there a way in TypeScript to declare that a variable is a keyof some Record without prior knowledge of the keys?

For instance, consider an API response returning JSON data. Is it possible to define a type for the keys of this payload to ensure that when indexing through the payload, the value will not be undefined? Or must we manually check for undefined each time we access a property in the payload object?

Here's an example:

const payload: Record<string, ValidValue> = await myApiCall()

const prop1 = 'a'
const prop2 = 'b'

if (isValidKey(prop1, payload)) {
  const value1 = payload[prop1] // here, `value1` should have type `ValidValue`
}

const value2 = payload[prop2] // here, `value2` should have type `ValidValue | undefined`

Answer №1

I managed to make it work by implementing a type guard function, but with a slight difference – instead of returning key is keyof MyMap, I defined a type for the payload object as

Record<string, ValidValue | undefined>
. The type guard now asserts
payload is Record<K, ValidValue>
, where K corresponds to the key parameter.

You can experiment with this solution on TS Playground.

// Type alias for convenience
type Payload = Record<string, ValidValue | undefined>;

const payload: Payload = await myApiCall()

function isValidKey<K extends string>(key: K, payload: Payload): payload is Record<K, ValidValue> {
  return key in payload;
}

const prop1 = 'a'
const prop2 = 'b'

if (isValidKey(prop1, payload)) {
  const value1 = payload[prop1] // the type of `value1` should evaluate to `ValidValue` ✅
}

const value2 = payload[prop2] // the type of `value2` should evaluate to `ValidValue | undefined` ✅

Answer №2

I believe I have stumbled upon a solution that seems to be functioning correctly in the TS playground.

const myMap = [1,2,3].reduce((p, n) => ({ ...p, [Math.random()]: n }), {})

type MyMap = typeof myMap

console.log('myMap', myMap)

function isKnownKey(key: string, thing: MyMap): key is keyof MyMap {
    return Object.keys(thing).includes(key)
}

const knownTypes = ['a', 'b']
const prop1 = Object.keys(myMap)[0]
const prop2 = 'b'

if (isKnownKey(prop1, myMap)) {
    const value1 = myMap[prop1]
    console.log('value1', value1)
}

const value2 = myMap[prop2]
/*
TS Error:
Element implicitly has an 'any' type because expression of type '"b"' can't be used to index type '{}'.
  Property 'b' does not exist on type '{}'.(7053)
*/

However, there's one aspect that is puzzling and eludes my understanding: value1 returns type never. Strangely, there are no TypeScript errors raised. What could possibly be happening in this scenario?

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

Can variables be declared for file paths within the HTML code in a TypeScript file?

We utilize the Vaadin designer to create the frontend of our project. Within the .ts files, we have images where we aim to establish variables for the file paths. Currently, the setup looks like this: <img src="../../themes/light/img/example.jpg&q ...

Using React Material UI to create multiple collapse components

Currently, I am facing an issue where all the collapses in my list are linked to one state for "open." This means that if I open one list, all the other lists also open. I am looking for a way to keep the collapses separate from each other without needing ...

Using Jasmine to simulate an if/else statement in Angular/Typescript unit testing

After making a minor change to an existing function, it has been flagged as new code during our quality checks. This means I need to create a unit test specifically for the new 4 lines of code. The challenge is that there was never a unit test in place for ...

Does this fall under the category of accepted practices for employing an effect in Angular?

I am working on integrating an Angular directive with a signal that receives values from a store selector. This signal is crucial for determining whether a button should be disabled or not. I'm curious about the best approach to listen to this signal ...

Demonstrate JSON data using ngFor loop in Angular

Need some assistance here. Trying to display data from a .json file using a ngFor loop. However, I keep running into the following error in my code: Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgF ...

Discover the power of TypeScript's dynamic type inference in functions

Below is a function that selects a random item from an array: const randomFromArray = (array: unknown[]) => { return array[randomNumberFromRange(0, array.length - 1)]; }; My query pertains to dynamically typing this input instead of resorting to u ...

Error: The code is unable to access the '0' property of an undefined variable, but it is functioning properly

I am working with two arrays in my code: bookingHistory: Booking[] = []; currentBookings: any[] = []; Both arrays are populated later in the code. The bookingHistory array consists of instances of Booking, while currentBookings contains arrays of Booking ...

I'm having trouble retrieving my variable within the socketcluster's socket.on function

How can I store the value of msg in the variable sample when sample is not accessible inside the callback function? import { Injectable } from '@angular/core'; import * as socketCluster from 'socketcluster-client'; @Injectable({ pro ...

unable to reinstall due to removal of global typing

After globally installing Moment typing with the command typings install dt~moment --save --global Checking the installed typings using typings list shows: ├── <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="93fffcf7f2e0 ...

Adjusting the transparency of TabBadge in Ionic 2

I am currently working on a project that involves tabs, and I'm looking to update the style of the badge when the value is 0. Unfortunately, I am unsure how to dynamically change the style of my tabs or adjust the opacity of the badge in the style. M ...

Separating the time and date into distinct variables offers flexibility in how they

Struggling with formatting time in a web component using TypeScript and React. The code below is working: new Date(myDate) .toLocaleTimeString( 'en-US', { weekday: 'short', year: 'numeric', month: 'short', ...

Sign up for notifications about updates to a variable within an Angular service

Is there a way to track changes in the value of a variable within an Angular service? I've been searching for a solution, but haven't been able to find one yet. In my layout's header component, I have a counter that displays the number of ...

Automatically adjust padding in nested lists with ReactJS and MaterialUI v1

How can I automatically add padding to nested lists that may vary in depth due to recursion? Currently, my output looks like this: https://i.stack.imgur.com/6anY9.png: However, I would like it to look like this instead: https://i.stack.imgur.com/dgSPB. ...

Taking out the modal element from the document object model, causing the animation

I am currently working on a project using Next.js, Typescript, and Tailwind CSS. Within this project, I have implemented a modal component with some animations. However, I encountered an issue where the modal does not get removed from the DOM when closed, ...

Mongoose encountered an error when attempting to cast the value "ObjectID" to an ObjectId at the specified path "red.s1"

My Mongoose schema is structured as follows: const gameSchema = new Schema({ matchNumber: { type: Number, required: [true, 'A match must have a number!'], unique: true }, red: { s1: { type: ...

Intermittent issue with Angular 2 encountered while following the Hero Editor tutorial on angular.io

I am encountering an occasional error in the console while following the angular.io tutorial using Mozilla Firefox. The error does not seem to impact the functionality or rendering of my application, and it only happens sporadically. If you could provide ...

Issue with narrowing TypeScript arrays often encountered

When working with arrays of strings in my TypeScript code, I restrict the contents to certain letters by using a defined type like ("A" | "B")[] for letters such as A and B. However, when I have a function that takes an arbitrary array ...

What's the best way to group rows in an angular mat-table?

I am working on a detailed mat-table with expanded rows and trying to group the rows based on Execution Date. While looking at this Stackblitz example where the data is grouped alphabetically, I am struggling to understand where to place the group header c ...

When clicking initially, the default input value in an Angular 2 form does not get set

I am currently learning angular2 as a beginner programmer. My goal is to create a form where, upon clicking on an employee, an editable form will appear with the employee's current data. However, I have encountered an issue where clicking on a user f ...

TypeScript Redux Thunk: Simplifying State Management

Seeking a deeper understanding of the ThunkDispatch function in TypeScript while working with Redux and thunk. Here is some code I found: // Example of using redux-thunk import { Middleware, Action, AnyAction } from "redux"; export interface ThunkDispatc ...