Specify the object key type when using a `for-in` loop

My current situation involves an object type:

interface ShortUrlParam {
    openid: string;
    avatar: string;
    nickname: string;
}

const param: ShortUrlParam = {
    openid: 'abc123',
    avatar: '',
    nickname: 'wenzi'
}

let query = '';
for(let key in param) {
    query += `&${key}=${encodeURIComponent(param[key])}`; // encountering an error here
}

When accessing param[key], I receive the following error message:

'Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'ShortUrlParam'. No index signature with a parameter of type 'string' was found on type 'ShortUrlParam'.ts(7053)

I have come up with two potential solutions, both not without flaws.

1. Redefining the interface ShortUrlParam

interface ShortUrlParam {
    openid: string;
    avatar: string;
    nickname: string;
    [key: string]: string;
}

2. Treating param as any

for(let key in param) {
    query += `&${key}=${encodeURIComponent((param as any)[key])}`;
}

My inquiry is whether there exists a more optimal solution to this issue?

Answer №1

Typescript does not allow type annotations for variables declared in a for..in loop due to the open nature of object types. This means that an object can have additional properties beyond what is expected, making it impossible to narrow down the type of a variable introduced in a loop.

However, there are workarounds available. One option is to declare the variable outside the loop with the desired type annotation:

let key: keyof ShortUrlParam;
for (key in param) {
  query += `&${key}=${encodeURIComponent(param[key])}`;
}

This approach ensures type safety, although caution should be taken if unexpected properties are added to the object later on.


An alternative method is to assert the type of the variable within the loop using type assertions:

for (let key in param) {
  query += `&${key}=${encodeURIComponent(param[key as keyof ShortUrlParam])}`;
}

Both approaches are more secure than using 'any', as they help catch errors where extra properties are added to the object. Hopefully, these suggestions prove helpful. Good luck!

Answer №2

Not quite as secure as jcalz's solution, but still handy for type hints when you don't have a specific type definition readily available (maybe because you didn't define an object literal right before). You can make an educated guess instead of resorting to any. Just stay away from any...

Furthermore, the JavaScript heap within a for loop gets reset after each iteration, so using const instead of let ensures that the key remains unchanged. If you do need to change the key being iterated over, stick with a traditional for-loop to keep your code clean.

Here is a general example:

for (const key in thing) {
  doThings(key as keyof typeof thing)
}

And here's an example relevant to your query:

for (const key in param) {
  query += `&${key}=${encodeURIComponent(param[key as keyof typeof param])}`
}

Answer №3

Attempt this:

for(let index=0;index<parameters.length; index++) {
  let keyValue = parameters[index];
  searchQuery += `&${keyValue}=${encodeURIComponent((parameters as any)[keyValue])}`;
}

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

What is the best approach for determining the most effective method for invoking a handler function in React?

As a newcomer to React, I am navigating through the various ways to define callback functions. // Approach 1 private handleInputChange = (event) => { this.setState({name: event.target.value}); } // Approach 2 private handleInputChange(event){ t ...

Angular 11 along with RxJS does not support the combineLatest method in the specified type

Hey there, I'm currently working on utilizing the combineLatest operator to merge two streams in Angular, but I keep encountering an error message stating that "combineLatest does not exist on type". I've attempted to move the code into a .pipe() ...

The data type 'null' is not a valid index type to be used in the Array.reduce() accumulator

This is a follow-up inquiry from: How can JavaScript convert multiple key-value pairs in object lists into one nested object? The initial objective was to merge numerous objects with various key-value pairs into a single nested object. For example, start ...

What sets enum with string values apart from a string type union in TypeScript?

When it comes to defining a variable with a predefined set of values in TypeScript code, there are two approaches - using an enum or using a union. For instance, imagine we have a button with various variants such as primary, secondary, and tertiary. We ...

Replace the default Material UI 5.0 typography theme with custom fonts on a global scale

My current challenge involves incorporating two personal fonts into the Material UI 5.0. My goal is to apply these fonts globally by overriding the theme settings. However, I have encountered issues with loading the fonts properly and modifying the typogra ...

What is the reason behind the lag caused by setTimeout() in my application, while RxJS timer().subscribe(...) does not have the same

I am currently working on a component that "lazy loads" some comments every 100ms. However, I noticed that when I use setTimeout for this task, the performance of my application suffers significantly. Here is a snippet from the component: <div *ngFor ...

Conflicting TypeScript enum types: numbers and numbers in NestJS/ExpressJS

Incorporating types into my NestJS server has been a priority. After creating a controller (a route for those who prefer Express), I attempted to define the type for params: public async getAllMessages( @Query('startDate', ValidateDate) start ...

Utilize Typescript/Javascript to utilize the Gmail API for sending emails via email

I am trying to send emails from my application using my Gmail account with Ionic. I have followed tutorials from SitePoint and Google Developers. Here is how I'm initializing the client: client_id: gapiKeys.client_id, discoveryDocs: ["https://www.goo ...

Identifying imports from a barrel file (index.ts) using code analysis

Can anyone help me understand how the Typescript compiler works? I am trying to write a script that will parse each typescript file, search for import declarations, and if an import declaration is using a barrel-file script, it should display a message. Af ...

DuplicateModelError: Unable to duplicate model after it has been compiled, React.js, MongoDB, TypeScript

In the early stages of developing an application using Next.js, Mongoose, and Typescript, I encountered a persistent issue. Whenever I attempt to send a request through Postman after clicking save, it fails, displaying the error message: OverwriteModelErr ...

Which one should I prioritize learning first - AngularJS or Laravel?

As a novice web developer, I am embarking on my first journey into the world of frameworks. After much consideration, I have narrowed it down to two options: AngularJS and Laravel. Can you offer any advice on which one would be best for me to start with? ...

Is there a way to render a component without having to render AppComponent constantly?

I am looking to display two components (AppComponent and UserComponent) without constantly displaying AppComponent. Here's how my code is structured: app.routing.module.ts const routes: Routes = [ { path: '', component: AppComponent ...

Retrieve the object from the data received from the HTTP GET API call

I have a question that has been asked before, but I am unable to achieve the desired result with my current approach. When my service retrieves data from an API, it returns results in the following format: { "nhits": 581, "paramete ...

Enhance autocomplete functionality by incorporating a left icon feature for text fields within the autocomplete component

I have a component with autocomplete functionality that displays tags Autocomplete with tags and I am trying to add a left icon, but only the right icon is functioning correctly. Current Issue When I add a left icon, it shows up but prevents the renderi ...

Bringing in AuthError with TypeScript from Firebase

Is it possible to verify if an error is of type "AuthError" in TypeScript when using Firebase? I have a Https Callable function with a try/catch block that looks like this: try { await admin.auth().getUser(data.uid); // will throw error if user doesn& ...

Acknowledgment Pop-up

When using the PrimeNG table with a custom modal component that I created, I encountered an issue. The edit functionality works correctly and retrieves the correct row id, however, the delete function always returns the id of the first row. dashboard.html ...

"An issue has been noticed with Discord.js and Discordx VoiceStateUpdate where the return

Whenever I attempt to retrieve the user ID, channel, and other information, I receive a response of undefined instead of the actual data import { VoiceState } from "discord.js"; import { Discord, On } from "discordx"; @Discord() export ...

Ways to eliminate the white background gap between pages on ionic

While developing an app using Ionic, I encountered a strange issue. Everything runs smoothly on a browser, but when testing the app on an Android 5 device, I noticed a white background appearing between pages. The app loads correctly with the custom splas ...

Validating a single field name with various DTO types based on conditions in a NestJS application

There is a field named postData in EmailTypeDto, but it has different types based on conditions. It may be confusing to explain in words, but the code makes it clear. export class EmailTypeDto { @IsEnum(EmailType) public type: EmailType; @ValidateIf ...

Encountered an issue when attempting to include a model in sequelize-typescript

I've been attempting to incorporate a model using sequelize-typescript: type AppMetaDataAttributes = { id: string; name: string; version: string; createdAt: string; updatedAt: string; }; type AppMetaDataCreationAttributes = Optional<App ...