What is the best way to restrict only valid paths to a nested object within a template literal type?

I am working with 2 objects and I need to dynamically generate a string based on the selected key. The second part of the generated string should be a key within the object associated with the selected key. In this scenario, only "test" or "test2" should be allowed as options after the "." in the code snippet t("common.").

const common = {
  "test": "Test",
  "test2": "Test2"
}

const greetings = {
  "hello": "Hello"
}

export type I18nMap = Record<typeof locales[number], I18nNamespace>;
export interface I18nNamespace {
    common: typeof common;
    greetings: typeof greetings;
}
export const locales = (["en", "nl"] as const);

type Interpolation = Record<string, string | number>

export function useTranslation<
  T extends keyof I18nNamespace,
  U extends T extends T ? keyof I18nNamespace[T] : never,
  V extends `${T}.${U}`
>(namespace: T | T[]): {t: (key: V, interpolation?: Interpolation) => void} {
  // ...
}

const { t } = useTranslation(["common", "greetings"])

// Only allow common.test | common.test2
t("common.")

Link to Playground

Answer №1

Consider utilizing this kind:

type GenerateI18nKeys<T extends keyof I18nNamespace> = T extends unknown
  ? `${T}.${Extract<keyof I18nNamespace[T], string>}`
  : never;

This function parses T and constructs a string type using T and keyof I18nNamespace[T]. Omitting the Extract<..., string> would trigger an error in TypeScript since property keys can include a symbol, which is not compatible with types allowed in a template literal (

string | number | bigint | boolean
).

In this scenario, the type parameters U and V are unnecessary.

export function useTranslation<T extends keyof I18nNamespace>(
  namespace: T | T[]
): {t: (key: GenerateI18nKeys<T>, interpolation?: Interpolation) => void} {
  // ...
}

const { t } = useTranslation(["common", "greetings"])

// Succeeds
t('common.test')
t('common.test2')
t('greetings.hello')

// Fails
t('common.hello')
t('greetngs.test')

Playground link

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's the reason behind my REST API delivering a 401 error code?

New Update After being encouraged to implement debug logs, I have discovered that the issue lies with Spring reporting an invalid CSRF token for the notices controller. I have compared the headers generated by Postman and the fetch requests, but found no ...

Potential absence of the object has been detected after performing object verification

I encountered an issue with the following error message: Object is possibly 'undefined'.ts(2532) This is what my configuration looks like: export interface IDataColumns { name: string; label: string; display: string; empty: boolean; fi ...

Making an Angular 6 HTTP GET call using HTTP-Basic authentication

When attempting to access a URL that requires Basic Authentication, and returns JSON data, what is the proper way to include my username and password in the following HTTP request? private postsURL = "https://jsonExample/posts"; getPosts(): Observable& ...

What measures can be taken to stop an event from being triggered from an external source?

Consider a scenario where there is a simple counting process functioning as a default Node EventEmitter: import {EventEmitter} from 'events'; async function sleep(milliseconds: number): Promise<void> { return new Promise((resolve) => ...

Eliminate the loading screen in Ionic 2

Within my app, there is a button that triggers the opening of WhatsApp and sends a sound. Attached to this button is a method that creates an Ionic loading component when clicked. The issue I am facing lies with the "loading.dismiss()" function. I want the ...

Issue with promise chaining detected in Mocha testing framework

Encountering an error when attempting to enter text using sendkeys or click a button in a popup with TypeScript promise chaining in Mocha. composeNewMessage(mailCount: number, recepientsName: any, subject: string, messageContent: string, recipientLink?: ...

What is the method to retrieve the data type of an array in TypeScript?

I am currently working on a TypeScript function (still getting acquainted with TS) that accepts a parameter which could be either a number, a string, an array of numbers, or an array of strings. It is crucial for me to distinguish between these 4 types wi ...

The specified main access point, "@angular/cdk/platform", is lacking in required dependencies

I recently updated my Angular app from version 8 to version 9. After resolving all compilation and linter errors, I encountered one specific issue that is causing me trouble: ERROR in The target entry-point "@angular/cdk/platform" has missing dep ...

Eliminating the need for RequireJS in the Typescript Visual Studio project template

After integrating RequireJS into my Typescript template using the nuget package manager, I found that it was more than what I needed and decided to uninstall it. Even though I removed the package through nuget and the files were deleted properly, my Typesc ...

How can we enhance the React.Component interface by adding a "direct" property?

Consider the following code snippet: interface Props extends React.HTMLAttributes { // ... } interface State { // ... } interface TextFieldComponent { field: HTMLInputElement | HTMLTextAreaElement } export default class TextField extends React.Co ...

Creating TypeScript unit tests for nested functions: A step-by-step guide

I'm currently working with Angular 16 in combination with the ngRx framework. My development involves TypeScript coding and writing unit tests (.spec.ts) using Jasmine, especially for code similar to the example below. How do I invoke this method with ...

Deduce the generic types of conditional return based on object property

My goal is to determine the generic type of Model for each property. Currently, everything is displaying as unknown[] instead of the desired types outlined in the comments below. playground class Model<T> { x?: T } type ArgumentType<T> = T ...

Establish a background image for a particular item within a list

Employing Ionic 2 with Typescript. I am seeking a way to empower users to choose the background color for each item in my list. ISSUE: How can I retrieve the reference to the i-th element? Whenever I select an item, it only changes the background of the ...

Is there a way to determine the return type without using a type variable?

I am striving to develop a function that can clone an array while maintaining the original data types. The current issue I am facing is that TypeScript is declaring the return type as any: export function cloneArray(inputArray) { return [...inputArray ...

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! ...

Error message "MODULE_NOT_FOUND occurring post typescript transpilation"

Upon building my typescript app and starting the app.js, I encountered this error: node:internal/modules/cjs/loader:1050 throw err; ^ Error: Cannot find module 'controllers' I suspect that the issue lies in how I am using import/export stat ...

Ionic has been experiencing issues with inaccurate boolean results being generated by Angular typescript

I have created a random number generator that requires the user to input a maximum and minimum value to generate a random number within that range. The application will show an error under two conditions: If either of the numbers entered is negative If t ...

The Ionic 5 app features a white iframe that functions perfectly on the web platform

Whenever I run my web application's function, the iframe is displayed. However, on Android, all I see is a white screen. Can anyone assist with resolving this issue? HMTL html <ion-content> <ion-button expand="full" color="warning" (clic ...

"The system is not paying attention to the Type/Interface used for verifying the contents of

Consider the interface declared within a controller: interface idAndAge { id : string, age : number } Now, let's look at the endpoint definition: @put('/tests') async replaceById( @requestBody() test: idAndAge,// <--to validate ...

Check for the presence of an event listener before adding it in Angular 5 using TypeScript

Currently, I am involved in developing a hybrid application using Angular. The form consists of components from both Angular 6 and AngularJS. This particular project is designed as a single-page application with data spread across ten different tabs. To en ...