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

In the event that you encounter various version formats during your work

Suppose I have a number in the format Example "1.0.0.0". If I want to increase it to the next version, it would be "1.0.0.1" By using the following regex code snippet, we can achieve this perfect result of incrementing the version to "1.0.0.1": let ver ...

Comparing JSON objects with JavaScript models: A guide

Currently I'm working with angular 2 and I have an array of data. data: MyModel[] = [ { id: 1, name: 'Name', secondName: 'SecondName' } In addition, I have created the interface MyModel: interface MyModel { id: number, nam ...

Creating an HTTP method handler function in Next.js API routes with an unspecified number of generic parameters

Looking to create a wrapper function in NextJS for API routes that can handle multiple HTTP methods with different handlers. For example, check out the TS playground interface GetResponse { hello: string, } // empty object type PostResponse = Record&l ...

ngPrime table column selection and data extraction

I am looking to extract multiple columns from a table. Can anyone suggest the best approach for this? Does NGPrime offer any functionality for achieving this task? Appreciate your help! ...

Implementing express-openid-connect in a TypeScript project

Trying to incorporate the express-openid-connect library for authentication backend setup with a "simple configuration," an issue arises when attempting to access the oidc object from express.Request: app.get("/", (req: express.Request, res: express.Respon ...

Entering the appropriate value into an object's property accurately

I am currently facing an issue with typing the value needed to be inserted into an object's property. Please refer below. interface SliceStateType { TrptLimit: number; TrptOffset: number; someString: string; someBool:boolean; } inter ...

A more efficient method for incorporating types into props within a functional component in a TypeScript-enabled NextJS project

When dealing with multiple props, I am looking for a way to add types. For example: export default function Posts({ source, frontMatter }) { ... } I have discovered one method which involves creating a wrapper type first and then defining the parameter ty ...

Receiving an error when attempting to inject the Router in a component constructor without using the elvis operator

Upon launching my app, I desire the route /home to be automatically displayed. Unfortunately, the Angular 2 version I am utilizing does not support the "useAsDefault: true" property in route definitions. To address this issue, I considered implementing th ...

How can I prevent node_module from being included when using the include directive in tsconfig.json?

Many developers are excluding the node_modules folder in their tsconfig.json. I, on the other hand, am using the include directive with specific folder patterns. Do I really need to exclude node_modules? And what about third-party libraries that aren' ...

Developing a dynamic modal using Angular and embedding Google Maps within an iframe

I'm currently working on implementing a modal in my Angular application that, when opened, displays Google Maps within an iframe. The problem I'm facing is that the iframe isn't loading and I'm receiving this error in the browser conso ...

How to access nested JSON elements in Javascript without relying on the eval function

Below is a JSON that I am trying to access. { "orders": { "errorData": { "errors": { "error": [ { "code": "ERROR_01", "description": "API service is down" } ] } }, "status": " ...

What made the "in" operator not the best choice in this situation?

When I set out to create a type that represents the values of a given object type, I initially came up with this: type Book = { name:string, year:number, author:string } // expected result "string" | "number" type ValueOf<T ex ...

I encountered TS2345 error: The argument type X cannot be assigned to the parameter type Y

Currently, I am delving into the world of Angular 8 as a beginner with this framework. In my attempt to design a new user interface with additional elements, I encountered an unexpected linting error after smoothly adding the first two fields. The error m ...

What enables typescript to be eligible for transpiling is its equivalent level of abstraction to javascript?

Transpilation is the act of converting code from one language to another with a similar level of abstraction. Can you point out some distinctive characteristics that indicate TypeScript transpires into JavaScript? ...

An interface that is extended by an optional generic parameter

I currently have an exported abstract class that has one generic. However, I now require two generics. I do not want to modify all existing classes that are using this class. Therefore, I am looking to add an optional generic class that extends an interfac ...

Understanding type inference in TypeScript

I'm attempting to grasp the concept of inferring generics in Typescript, but I can't seem to figure out where I'm going wrong. Although my concrete example is too large to include here, I've provided a link to a small TypeScript playgro ...

What is the best way to implement an interface for accurately checking each prop type?

Currently, while working with Typescript, I am looking for a solution to define an interface with specific properties inside my object of marks. At the moment, I am using "any", but I know there must be a better approach. Any guidance or advice on how to p ...

Is there a way to specifically target the MUI paper component within the select style without relying on the SX props?

I have been experimenting with styling the Select MUI component using the styled function. I am looking to create a reusable style and move away from using sx. Despite trying various methods, I am struggling to identify the correct class in order to direct ...

Arrange objects in dropdown menu to line up

I'm currently working on a dropdown menu and I have a specific requirement – the menu should always be split into two columns and be able to span multiple lines. However, I've encountered an issue where the columns are not aligned properly, cau ...

A guide to effectively utilizing a TypeScript cast in JSX/TSX components

When trying to cast TypeScript in a .tsx file, the compiler automatically interprets it as JSX. For example: (<HtmlInputElement> event.target).value You will receive an error message stating that: JSX element type 'HtmlInputElement' is ...