Can an object's keys be strongly typed according to array values?

To utilize normalized data effectively, I have created an object with keys that can only be a list of numbers within a specified array. Is there a way to enforce this restriction in typing so that if I attempt to access the object using a non-array key, an error is triggered (rather than just the possibility of encountering an undefined value)?

In simpler terms, consider an object of type Foo:

 const x:Foo = {                                                 
   allIds: [1,3],                                                                  
   structured: {                                                                    
     1: "foo",          
     3: "bar"           
    } 
 }                       

Is it feasible to define Foo in a manner such that the following operation results in an error due to '2' not being present in the allIds array?

const z = x.structured[2]

I've attempted the following approach, but it seems ineffective as the index signature expands to "number":

 interface Foo {                                                    
   allIds: number[]                                                               
   structured: Record<Foo["allIds"][number],string>                   
 }    

Any assistance on this matter would be greatly appreciated.

Playground

Please note: The values within allIds are statically defined in the code and do not change at runtime.

Answer №1

If you want to avoid typing x with interface Foo, consider using const assertions. By using const assertions, you can prevent the automatic widening of allIds to number[] and instead keep it as readonly [1,3].

 const x = {                                                 
   allIds: [1,3] as const,                                                                  
   structured: {                                                                    
     1: "foo",          
     3: "bar"           
    } 
 } 

In case your object x is already divided into two parts - one statically defined allIds and possibly dynamic part

structured</code, you can expand the interface <code>Foo
by introducing a generic parameter AllowedIndices with the type provided from allIds that has been asserted as const.

const allIds = [1,3,4] as const;

 interface Foo<AllowedIndices extends readonly number[]> {                                                    
   structured: Partial<Record<AllowedIndices[number], string>>                
 }                                                                                  
                                                                                    
 const x: Foo<typeof allIds> = {                                                 
   structured: {                                                                    
     1: "foo",          
     3: "bar"           
    } 
 }                  

const z = x.structured[2] // error   

Playground Link

Note:

An additional index 4 has been included to demonstrate the use of Partial<T>: indices in allIds may not necessarily correspond to keys in x.structured.

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

Cannot perform table inserts or creates with NestJS Sequelize functionality

I am currently in the process of setting up a small web server with a MySQL database. To achieve this, I am utilizing NestJs along with Sequelize. However, as I am still in the learning phase, I seem to be encountering an error: Within my database, I have ...

Using jest-dom without Jest is definitely an interesting challenge that many developers may

Can anyone help me with extending Typescript interfaces? I have come across a situation that I am trying to solve. In my tests, I am utilizing expect without using Jest directly (I installed it separately and it functions properly). Now, I am interested ...

What is the best way to pass a state within a route component in react-router?

... import { useNavigate, NavigateFunction } from "react-router"; ... function Form(): JSX.Element { const navigateToCountry = (country: string) => { // Code to navigate to country page with the given country } const [selectedCount ...

Unable to load HomeComponent in Angular 4 due to an error

Currently working on creating an app using the "An API of Ice and Fire". Encountering an issue with a displayed error message "ERROR Error: "[object Object]"" when attempting to redirect to homeComponent. Attached are screenshots displaying the error: B ...

Consider pushing items onto an array only once when the condition is met, instead of adding to the array every

I have been tasked with importing Excel files containing customer orders into my web application. The process involves converting the data in the file into an object of arrays, where each array represents a row from the Excel sheet. Once the data is impor ...

What is preventing me from using the "@" symbol to substitute the lengthy relative path in my __tests__ directory?

https://i.sstatic.net/U1uW3.png When I initialized my Vue project using vue-cli, I encountered an issue where the use of @ in my src folder was functioning correctly, but not in my __tests__ folder. I have already added configurations to my tsconfig.json ...

The error message "TypeError: Attempting to access the 'map' property of an undefined value (React JS)" was displayed

I'm currently working on a recursive function that needs to iterate over an object type called ItemType. However, I encountered an error message: TypeError: Cannot read property 'map' of undefined This is the code snippet causing the issue: ...

What is the proper method for specifying the path to my index.tsx file within a React application?

After using npx create-react-app my-app --template typescript to generate my React app, I decided to reorganize the files by moving them to /src/client and relocating my Express backend to /src/server. However, upon running the relocated React app, I encou ...

Resolving DOMException issue in Google Chrome: A Step-by-Step Guide

In my browser game, I am experiencing an issue with sound playback specifically in Google Chrome. Although the sound manager functions properly in other browsers, it returns a DOMException error after playing sounds in 50% of cases. I'm unsure what co ...

What is the best way to limit the type of the second argument based on the type of the

Within the tutorial Exploring How to Extract Parameter Types from String Literal Types Using TypeScript, a fascinating problem is presented without a solution. function calculate(operation, data) { if (operation === 'add') { return da ...

Suggestions for managing the window authentication popup in Protractor when working with Cucumber and TypeScript?

I'm a beginner with Protractor and I'm working on a script that needs to handle a window authentication pop-up when clicking on an element. I need to pass my user id and password to access the web page. Can someone guide me on how to handle this ...

How can you display or list the props of a React component alongside its documentation on the same page using TypeDoc?

/** * Definition of properties for the Component */ export interface ComponentProps { /** * Name of something */ name: string, /** * Action that occurs when component is clicked */ onClick: () => void } /** * @category Componen ...

The code breaks when the lodash version is updated to 4.17.4

After updating lodash to version 4.17.4, I encountered an error in Typescript that says: TypeError: _.uniqBy is not a function Uncaught TypeError: _.split is not a function The code snippet in question is as follows: import * as _ from 'lodash&apo ...

Error encountered while building with Next.js using TypeScript: SyntaxError - Unexpected token 'export' in data export

For access to the code, click here => https://codesandbox.io/s/sweet-mcclintock-dhczx?file=/pages/index.js The initial issue arises when attempting to use @iconify-icons/cryptocurrency with next.js and typescript (specifically in typescript). SyntaxErr ...

Error TS2307: Module 'calculator' could not be located

Running a Sharepoint Framework project in Visual Studio Code: This is the project structure: https://i.stack.imgur.com/GAlsX.png The files are organized as follows: ComplexCalculator.ts export class ComplexCalculator { public sqr(v1: number): number ...

Guide to Reverting the Two-Way ngModel Binding Data in Angular 2

I am utilizing a form in angular 2 that includes two-way binding data value ([(ngModel)]) to enable both edit and add functionality. When a user selects the edit option on the listing page and modifies the input, the new values automatically appear on the ...

Encountered an ERROR when attempting to deploy a next.js app to Azure Static Webapp using GitHub actions for

I am encountering an issue that is causing some frustration. The problem only arises during my github actions build. Interestingly, when I run the build locally, everything works perfectly and I can access the route handler without any issues. However, eve ...

Uncover the mystery behind the return value of a generic function in TypeScript

I can't seem to wrap my head around why TypeScript is behaving in the way described below. Snippet 01| const dictionary: { [key: string]: unknown} = {} 02| 03| function set<T>(key: string, value: T): void { 04| dictionary[key] = value; 05| } ...

Understanding Typescript in Next.js components: Deciphering the purpose behind each segment

Consider the following function: type User = { id: string, name: string } interface Props { user: User; } export const getUserInfo: GetUserInfo<User> = async ({ user }: Props) => { const userData = await fetchUser(user.id); return ...

What is the best way to create a sortable column that is based on a nested object within data.record?

I am utilizing jquery jtable to showcase some data in a table. I have been using the following code snippet for each field in the table to display the data and enable sorting: sorting: true, display: (data) => { return data.record.<whatever_value ...