Derive data type details from a string using template literals

Can a specific type be constructed directly from the provided string?

I am interested in creating a type similar to the example below:

type MyConfig<T> = {
  elements: T[];
  onUpdate: (modified: GeneratedType<T>) => void;
}

const configuration: MyConfig = {
  elements: ['id', 'nested.id', 'nested.name'],
  onUpdate: modified => {
    console.log(`You have updated ${modified.nested.name}(id: ${modified.nested.id})`);
  },
};

This will result in generating a type for modified as

{id: string; nested: { id: string; name: string}}

Answer №1

While this solution may not be flawless, the updated type appears to be accurate:

type First<T extends string> = T extends `${infer L}.${string}` ? L : T
type Nested<T extends string> = T extends `${string}.${infer R}` ? R : string

type _ConstructedType<T extends string> = string extends Nested<T> ? string : {
  [Key in T as First<Nested<T>>]: _ConstructedType<Nested<T>>
}

type ConstructedType<K extends readonly string[]> = {
  [Key in K[number] as First<Key>]: _ConstructedType<Key>
}

function createConf<K extends readonly string[]>(conf: {items: K, onChange: (updated: ConstructedType<K>) => any}) {
    return conf
}

createConf({
  items: ['id', 'nested.id', 'nested.name'] as const,
  onChange: updated => {
    console.log(`You updated ${updated.nested.name}(id: ${updated.nested.id})`);
  },
})

In your inquiry, you indicated the desire for a MyConfiguration type. However, a type on its own cannot enforce property constraints. Thus, a factory function named createConf is implemented to address this. By passing a conf object to the function, all types are inferred accordingly.

An existing limitation that remains unresolved involves appending as const after the items array. Failing to do so will result in TypeScript inferring the incorrect type as string[] instead of a tuple.

Playground


Credit goes to @jcalz for correcting various issues with the code:

type ConstructedType<K extends string> = {
  [P in K as P extends `${infer L}.${string}` ? L : P]:
  [P] extends [`${string}.${infer R}`] ? ConstructedType<R> : string;
}

function createConf<K extends string>(conf:
  { items: readonly K[], onChange: (updated: ConstructedType<K>) => any }) {
  return conf
}

type Test = ConstructedType<'id' | 'nested.id' | 'nested.child.name'>

const x = createConf({
  items: ['id', 'nested.id', 'nested.name', 'nested.child.name'],
  onChange: updated => {
    console.log(`You updated ${updated.nested.name}(id: ${updated.nested.id})`);
  },
})

An alternative solution provided by @jcalz can be found here.

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

Troubleshooting: React.forwardRef and Typescript defaultProps not functioning correctly

I am currently working on migrating components from a js to ts react component library for my own project. The library was originally written in js using a customized material-ui library. My task now is to migrate these components one by one. Here is an ...

I am encountering a problem with my component as the Angular Directive is missing

Recently, I incorporated a customized directive into my Angular app to allow file uploads via drag and drop. However, I encountered an issue where the command line kept throwing an error stating that my function does not exist within my component. Propert ...

Guide to transforming API Response into Custom type in Angular 5

Describing my method to structure the API Response - interface MyTest { property2: string } Incorporating Angular 5 Service Code - getAPI(searchKey: string) { this.productsAPIUrl = https://localhost:44331/api/SampleData/WeatherFore ...

Sharing markdown content between two Vue.js components

I have a markdown editor in View A which is displaying the result in the current View. My goal is to share this result with another page, View B. In View A, there is a button that allows the user to share the markdown result with View B. I am using a texta ...

The FirebaseX Ionic native plugin received 2 arguments instead of the expected 3-4

Trying to implement Firebase Phone Auth with the FirebaseX plugin, I encountered an issue. Here is the code snippet I used: async getVerificationCode(): void { const res:any = await this.firebaseX.verifyPhoneNumber('+16505553434', 60); ...

Leveraging editor.action.insertSnippet from a different plugin

I am attempting to enhance the functionality of VS Code by adding buttons to the status bar that automatically insert code snippets. I am utilizing this Extension for this purpose. Additionally, I have configured keybindings in my keybindings.json file whi ...

What is the correct way to close an ngx-contextmenu in an Angular application?

In my angular project, I implemented an ngx-contextmenu. Within one of my components, the template includes the following code: <div... [contextMenu]="basicMenu"> <context-menu>..... </div> After some time, the component with the conte ...

I am looking to conceal the y-axis labels and tooltip within the react chart

I am currently working with react-chart-2. I have a line graph that displays a tooltip when hovered over, but I would like to hide this tooltip feature. Additionally, I want to remove the numbers 0, 0.1, 0.2 up to 1 on the left side (y-axis) of the gra ...

The new experimental appDir feature in Next.js 13 is failing to display <meta> or <title> tags in the <head> section when rendering on the server

I'm currently experimenting with the new experimental appDir feature in Next.js 13, and I've encountered a small issue. This project is utilizing: Next.js 13 React 18 MUI 5 (styled components using @mui/system @emotion/react @emotion/styled) T ...

What is the recommended approach for managing state in React when multiple components are trying to access and modify its data at the same time?

Issue: I am experiencing difficulty in adding new keys and/or values to the JSON editor or YAML editor when they both share and update the same state. The parent component sends JSON data to the child component through props import * as React from 'r ...

Is there a way to access the element reference of a component directly within the template?

When I mouse over certain elements, I use the following code to set focus: <div #divTemplateVar (mouseover)="divTemplateVar.focus()"></div> However, this method does not work for components: <component #componentTemplateVar (mouseover)="c ...

Encountering a TS2739 error while retrieving data in an Angular service function

In my code, I have created a function to fetch objects from my dummy data and assign them to a variable. setData(key: string) { let dataChunk: ProductIndex = PRODUCTDATA.filter(a => {a.productId == key;}); this.ProductData = dataChunk; } The i ...

Encountered an issue while trying to read the property 'temp' of undefined within an HTML document

Can someone help me with this issue? I'm facing an error with the JSON data retrieved from an API: ERROR in src/app/weather/weather.component.ts(39,30): error TS2339: Property 'main' does not exist on type 'Iweather[]' Here is ...

Solve TypeScript React error TS(2339) by resolving issues with extending a React.FC and using static property of type JSX.Element for uninitialized components

Currently, in VSCode, the typescript compiler is at TypeScript React 4.4.2 and it's pointing to a TS(2339) error: Property 'Col' does not exist on type 'FC<GridProps>'. I have attempted to add this prop to GridProps: export ...

Organize routes into distinct modules in Angular 6

Currently grappling with routing in my Angular 6 application. Wondering if the structure I have in mind is feasible. Here's what it looks like: The App module contains the main routing with a parent route defining the layout: const routes: Routes = ...

What are the steps to access a query parameter within an API route.js file using the latest App routing strategy?

Here is the goal I am aiming for: Utilize Next.js with App router. Establish a backend route /api/prompt?search=[search_text] Retrieve and interpret the search query parameter in my route.ts file. Based on the search parameter, send back data to the front ...

What is the best way to employ document.addEventListener in TypeScript?

I am currently learning Ionic v2 and I am using document.addEventListener, but I am encountering errors as shown below: > [11:10:21] ionic-app-scripts 0.0.47 [11:10:21] build dev started ... [11:10:21] clean started ... [11:10:21] clean finished in ...

"An error occurred: Uncaught SyntaxError - The import statement can only be used within a module. Including a TypeScript file into a

I need to integrate an Angular 10 TypeScript service into a jQuery file, but I am facing an issue. When I try to import the TypeScript service file into my jQuery file, I encounter the following error: Uncaught SyntaxError: Cannot use import statement outs ...

What are the steps to organize an array of objects by a specific key?

Experimented with the following approach: if (field == 'age') { if (this.sortedAge) { this.fltUsers.sort(function (a, b) { if (b.totalHours > a.totalHours) { return 1; } }); this ...

Is Typescript pass by value or pass by reference?

I have these files: data.ts: export const myData { info1: "info1", info2: "info2", ... ... } and I also have this class: my-class.ts export class MyClass { private data: any; constructor(data: any) { this.data = data ...