Verifying data types on combined function parameters

In the process of creating a function called getLocale(obj, key, locale), I aim to retrieve a specific locale from an object based on a given key. If the desired locale variant is not available, it should return a fallback option.

While adding type checking to this function, I encountered a challenge in merging the key and locale arguments to effectively create the keyof obj:

Here's my data:

const data = {
    name_nl: 'Naam',
    name_en: 'Name',
}

The function looks like this:

const allowedLangs = ['nl', 'en'] as const;
function getLocale<T extends Record<K, any>, K extends keyof any>(
    data: T,
    key: K,
    locale: typeof allowedLangs[number]
): T[K] | 'not-found' {
    return data[`${key}_${locale}`] || data[`${key}_en`] || 'not-found';
}

When calling the function:

getLocale(data, 'name', 'nl'); // Naam

However, TypeScript raises an error stating that

Property 'name' is missing in type
. This is because K does not directly represent a keyof
T</code in the function; rather, it's a combination of <code>locale
and key.

Is there a solution to merge these arguments seamlessly?

Answer №1

To tackle this issue, you have the freedom to adjust your function signature.

function getLocale<
  T extends Record<`${K}_${typeof allowedLangs[number]}`, any>, 
  K extends string
>(
    data: T,
    key: K,
    locale: typeof allowedLangs[number]
) {
    return data[`${key}_${locale}`] || data[`${key}_en`] || 'not-found';
}

An alteration can be made by constraining K to just a string, instead of using keyof any. Utilize a template literal string type in the Record of T to ensure that the key starts with K and ends with typeof allowedLangs[number].

Omitting the return type of the function allows TypeScript to infer it automatically. The inferred return type will look like this:

T[`${K}_nl` | `${K}_en`] | T[`${K}_en`] | "not-found"

This ensures complete validation when invoking the function.

getLocale(data, 'name', 'nl');
getLocale(data, 'abc', 'nl'); // Error
getLocale(data, 'name', 'de'); // Error

Playground

Answer №2

Implement the concept of Template Literal Types.

type Language = 'nl' | 'en'
type Info<T extends string> = Record<`${T}_${Language}`, any>

function getInfo<T extends string>(
    data: Info<T>,
    key: T,
    lang: Language
): [T] | 'not-found' {
    return data[`${key}_${lang}`] || data[`${key}_en`] || 'not-found';
}


const result = getInfo({
    name_nl: 'Naam',
    name_en: 'Name',
}, 'name', 'nl');

Test it out on TypeScript playground.

Answer №3

Consider updating the allowed languages to an enum data type for more accurate typing. The current method of inferring the type is incorrect

locale: typeof allowedLangs[number]

Instead, it should be

locale: ArrElement<typeof allowedLangs>

The issue you may be encountering lies in this line

key: K,

Unfortunately, there are no better typing options available than: key: string

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

Angular 14: Trouble with ngFor after latest update - Type 'unknown' causing issues

Just updated my project to angular version 14.0.4 Within the html of a component, I have the following code: <div class="file" *ngFor="let file of localDocumentData.files; index as i;"> <div class="card"> ...

How can I create an Array of objects that implement an interface? Here's an example: myData: Array<myInterface> = []; However, I encountered an issue where the error message "Property 'xxxxxx' does not exist on type 'myInterface[]'" appears

Currently, I am in the process of defining an interface for an array of objects. My goal is to set the initial value within the component as an empty array. Within a file, I have created the following interface: export interface myInterface{ "pictur ...

Keep an ear out for updates on object changes in Angular

One of my challenges involves a form that directly updates an object in the following manner: component.html <input type="text" [(ngModel)]="user.name" /> <input type="text" [(ngModel)]="user.surname" /> <input type="text" [(ngModel)]="use ...

Is it possible to encounter an unusual token export while trying to deactivate Vue with veevalidate

Utilizing Nuxt with server side rendering. Incorporating Typescript along with vee-validate version 3.4.9. The following code has been validated successfully extend('positive', value => { return value >= 0; }); Upon adding the default, ...

Utilizing a loaded variable containing data from an external API request within the useEffect() hook of a React component

Essentially, I have an API request within the useEffect() hook to fetch all "notebooks" before the page renders, allowing me to display them. useEffect(() => { getIdToken().then((idToken) => { const data = getAllNotebooks(idToken); ...

Setting up roles and permissions for the admin user in Strapi v4 during the bootstrap process

This project is built using Typescript. To streamline the development process, all data needs to be bootstrapped in advance so that new team members working on the project do not have to manually insert data into the strapi admin panel. While inserting ne ...

Encountering a type error while trying to read dummy data, as the map function is not

I am currently working on fetching dummy data from a URL in my component using TS and Next.js. Unfortunately, I encountered an error type that I am unable to diagnose. typings.d.ts: export type Themen = { id: number; title: string; description: string; ...

Why won't the sound play on the button with the picture?

I am currently working on a website project that requires buttons with pictures and sound. Despite my efforts, the sound feature is not functioning properly in Chrome and Firefox. I am still learning and would like to know how to toggle the sound on and of ...

Retrieve the value of a promise and transfer it to the subsequent function of an observable

For my Angular 9 application, I have created a custom interceptor called AuthorizationHeaderInterceptor: @Injectable() export class AuthorizationHeaderInterceptor implements HttpInterceptor { constructor(private authenticationService: AuthenticationSer ...

It takes a brief moment for CSS to fully load and render after a webpage has been loaded

For some reason, CSS is not rendering properly when I load a webpage that was created using React, Next.js, Material UI, and Styled-components. The website is not server-side rendered, but this issue seems similar to what's described here You can see ...

Issue encountered with the inability to successfully subscribe to the LoggedIn Observable

After successfully logging in using a service in Angular, I am encountering an error while trying to hide the signin and signup links. The error message can be seen in this screenshot: https://i.stack.imgur.com/WcRYm.png Below is my service code snippet: ...

Troubleshooting the inclusion of nodemon in package.json

I am interested in implementing nodemon to automatically recompile the project when there are changes made to the code during development. package.json { "name": "insurance-web-site", "version": "0.1.0", " ...

Angular: Effective communication between components through routing and Observable binding ultimately results in the advancement of ngtsc(233

I designed an Angular Component named "crear-pedido" that exhibits a catalog of items (using row of products) and my aim is for the user to have the ability to click on the values in the ID column and navigate the application to a subordinate component kno ...

The depth buffer in Webgl FrameBuffer is not being cleared properly

Currently, I am working on developing a 2D sprite renderer that utilizes render textures for custom compositing. However, I have encountered an issue where the depth buffer on the FrameBuffer is not clearing properly. Due to this, all the sprites leave a p ...

What could be causing the QullJS delta to display in a nonsensical sequence?

The outcome showcased in the delta appears as: {"ops":[{"retain":710},{"insert":" yesterday, and she says—”\n“The clinic?","attributes":{"prediction":"prediction"}},{"del ...

Having trouble with the service connection in Stackblitz?

Objective: I am trying to establish a connection with the Data service in StackBlitz. Issue: Unfortunately, my attempts are not successful. Can anyone pinpoint what I am overlooking? Project Link: https://stackblitz.com/edit/angular-mpy6pr Many th ...

Incorporating TypeScript into a project that already contains .js files

I currently have a NodeJS project with .js files written in ES6. I am interested in integrating typescript into this existing project. What steps should I follow? Can I simply introduce .ts files within the same directory structure? One issue I have encou ...

"Exploring the New Feature of Angular 17: Named Router Outlets Implemented

One issue I am facing with my application is the rendering of different pages based on whether a user is logged in or not. The generic pages like the landing or logout page should be displayed within the primary router-outlet when the user is not logged in ...

filtering an array based on a specific property will result in the original array remaining

Working on filtering an array of objects based on a certain property using the following code snippet: if (payment == Payment.CREDIT_CARD) { this.currenies.filter((currency: Currency) => currency.isFromEurope === true); console.log(this.currencies) ...

Issue encountered while creating Next.js 13.4 application: The type 'ResolvingMetadata | undefined' does not meet the requirement of 'ResolvingMetadata'

I'm currently working on dynamically generating metadata for my Next.js 13.4 application using this code snippet: export async function generateMetadata( { params, searchParams }: Props, ) { try { // extract the route parameters ...