What is the most efficient method for defining a string structure in TypeScript?

Consider the following example of a JavaScript object:

const myObject = {
   id: 'my_id', // Base value which sets the structure for the following values (always pascal case). 
   upperID: 'MY_ID', // Uppercase version of `id`.
   camelID: 'myId', // Camel case version of `id`.
}

I am looking to implement TypeScript to enforce that id remains in pascal case. The values of upperID and camelID should also reflect this structure variation, as shown above. How would you suggest declaring the type for this myObject in TypeScript?

Answer №1

Transforming a string to upper case is a breeze with the handy Uppercase<T> utility type.

type UppercaseString<STR extends string> = {
    original: STR,
    upperCased: Uppercase<STR>
}

const myString: UppercaseString<'hello_world'> = {
   original: 'hello_world',
   upperCased: 'HELLO_WORLD',
} as const

See playground


Now, let's dive into transforming a string to camel case, which is a bit more complex.

Here's one approach to achieving this.

type CamelCase<STR extends string> =
    STR extends `${infer First}_${infer Letter}${infer Rest}`
        ? `${First}${Capitalize<Letter>}${CamelCase<Rest>}`
        : STR

type TestCamelCase = CamelCase<'hello_world_of_typescript'> // 'helloWorldOfTypescript'

See playground

Let's break it down.

This generic type takes a string type as the generic parameter STR. It then checks if STR matches a certain pattern with an underscore followed by some characters.

If it does, it infers substrings from that pattern. Extracting the part before the underscore as First, the character after the underscore as Letter, and the rest of the string as Rest. Otherwise, it returns the string as is.

Then it builds a new string by combining First, the capitalized Letter, and Rest. This process repeats recursively until there are no more underscores left.


With that in place, finishing up is a breeze:

type CamelCase<STR extends string> =
    STR extends `${infer First}_${infer Letter}${infer Rest}`
        ? `${First}${Capitalize<Letter>}${CamelCase<Rest>}`
        : STR

type CasedStrings<STR extends string> = {
    original: STR,
    upperCased: Uppercase<STR>
    camelCased: CamelCase<STR>
}

const myString: CasedStrings<'hello_world'> = {
   original: 'hello_world',
   upperCased: 'HELLO_WORLD',
   camelCased: 'helloWorld',
} as const

See playground


For convenience, you may want a function to automate this process:

function transformString<STR extends string>(string: STR): CasedStrings<STR> {
    return {} as unknown as CasedStrings<STR> // placeholder for actual implementation
}

const transformedString = transformString('hello_world')
transformedString.original       // type: 'hello_world'
transformedString.upperCased     // type: 'HELLO_WORLD'
transformedString.camelCased     // type: 'helloWorld'

See playground

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 could be causing the code to produce 4 elements instead of the expected 2?

I'm trying to understand why the code above is creating four new paragraphs instead of just two. Can someone please explain what exactly happens in the $("p").before($("p").clone()); part? <!DOCTYPE html> <html> <head> <script ...

Oops! Looks like there's a hiccup with the express-validator plugin as the validation fails due to req.checkBody not being recognized as

Currently, I am setting up a post route to handle a submit request. The code snippet provided is from my user.js route: var express = require('express'); var router = express.Router(); var multer = require('multer'); var upload = multe ...

Framework7 initialization not triggering when page is initialized

Unable to get init function working in my Framework 7 app. I have attempted all the solutions provided in the following link: Framework7 Page Init Event Not Firing // Setting up Application var myApp = new Framework7({ animateNavBackIcon: true, ...

Exploring the intersection of onBeforeUnload and node.js

I just started learning about node.js. I'm curious if onbeforeunload can be used in node.js. If so, will the "no script" plugin for Firefox block scripts created by node.js? I want to inform my visitors with a message before they leave the page withou ...

When the VueJS element is rendered on Chrome addon, it suddenly vanishes

I have been using a developer mode addon successfully in Chrome for quite some time. Now, I want to add a button to my Vue-powered page. Here's the code snippet: let isletCtl = document.createElement('div') isletCtl.id ...

There is no link between the two containers

I am facing an issue where two containers need to connect with each other. However, when attempting to fetch data from one container, I encounter an ENOTFOUND error. Surprisingly, this code functions properly on my local system but fails within the contain ...

Limit Typescript decorator usage to functions that return either void or Promise<void>

I've been working on developing a decorator that is specifically designed for methods of type void or Promise<void>. class TestClass { // compiles successfully @Example() test() {} // should compile, but doesn't @Example() asyn ...

Parsing a JSON array using Moment.js within a subscription function

I have a series of time entries within a data JSON array that I need to parse and format using Moment.js. The formatted data will then be stored in the this.appointmentTimes array for easy access on my view using {{appointmentTime.time}}. Here is the JSON ...

Display or conceal a div depending on the selected radio button

I am attempting to display a hidden div based on the input of a radio button. If the "yes" option is checked, the hidden div should be shown. Conversely, if the "no" option is checked, the div should be hidden. I'm not sure why this code isn't w ...

Tips for showcasing a table generated from various input types on a separate page after pressing the submit button

After creating two JavaScript functions, I am eager to utilize both of them upon pressing the submit button on my form. The first function is already integrated into the submit button and activates a paragraph display upon submission. Now, I intend to sh ...

Turning a JSON dot string into an object reference in JavaScript: A simple guide

Having a JSON object labeled test with values like this: {"items":[{"name":"test"}]}, I need a way to apply the string items[0].name to it in order to search for a specific value (test.items[0].name). Currently, my only idea is to create a function that pa ...

Deciphering a mysterious message in Typescript while defining a function for one of my tasks

Currently, I am working with a stack that includes React, TypeScript, and Redux. Unfortunately, I have encountered an issue while using an interface for one of my actions. The error message I received is quite cryptic: Duplicate identifier 'number&apo ...

A problem arises when utilizing jQuery's $.isArray functionality

Successfully executing an AJAX post, the function test is utilized to process the passed data. $("#formID").submit(function (event) { $('#postError').html('').hide(); $('#postInfo').html('loading results').s ...

JQuery fails to remove the "display:hidden" attribute

List of methods I attempted: Utilizing .removeClass and addClass functions Applying show() and hide() methods Modifying the CSS properties Initially, I confirmed that my script contains onLoad. Furthermore, all other elements are functioning correctly. D ...

Create a new storefront JavaScript plugin for Shopware 6 to replace the existing one

I am attempting to replace an existing JavaScript plugin in Shopware 6. However, the code within the plugin file does not seem to execute. Here is my main.js: import MyCookiePermissionPlugin from './plugin/my-cookie-permission/my-cookie-permission.pl ...

It seems that there is a null value being returned in the midst of the

I have developed a model using express. While setting this in one function, it returns null in another function, forcing me to use return. What is the proper method to handle this situation? const Seat = function(seat) { this.seat = seat.seat; this. ...

Close the material-ui popper when clicking away

I am currently working on implementing a Material UI popper feature that should close when the user clicks outside of it using ClickAwayListener. However, I have been unable to make this functionality work despite trying different approaches. I've att ...

Storing an ID field across different domains using HTML and JavaScript: Best Practices

Currently, my web app includes a conversion tracking feature that analyzes whether activity "A" by a website visitor leads to action "B." This feature works effectively when the tracking is contained within a single domain but encounters issues across mul ...

The issue of the Angular service being consistently undefined arises when it is invoked within an

I have already researched numerous other SO questions, but none of the solutions worked for me. My goal is to implement an async validator that checks if a entered username already exists. However, every time I type a letter into the input field, I encoun ...

Address properties that are undefined before they cause an error

Is there a smart way to manage undefined properties and/or identifiers on an object before they result in failure/returning undefined? Can we intercept access to a non-defined property and address it without resorting to try/catch blocks? var myObj = { ...