TypeScript implements strong typing for object strings

Is it possible to create a custom type in TypeScript that represents a stringified object with a specific structure?

Instead of just defining type SomeType = string, I would like to define a type that looks something like this:

type SomeType = Stringified<{
           prop1: 'value1', 
           prop2: 'value2'
         }>

Although ultimately the value of type SomeType is a string, I want to enforce a specific format for that string.

Even if TypeScript cannot strictly enforce this custom type and will still accept any string, I believe it still adds clarity and readability to the code.

Is there a way to achieve this kind of custom type in TypeScript?

Answer №1

It is assumed that the literal types of the stringified values are not known at compile time. This means that dealing with TypeScript code in which the compiler validates whether a string literal conforms to a specific type is not expected.

// Example of what is not expected
const stringifiedThing: SomeType = "{\"prop1\": \"value1\", \"prop2\": \"value2\"}";

If the assumption is incorrect and validation at compile time is necessary, template literal types could be explored. However, implementing parsers with template literal types can be complex. There may not be a specific type that accurately represents SomeType using pattern template literal types.

type SomeType = `{"prop1": "${string}", "prop2": "${string}"}`;

Although this approach has limitations, such as accepting incorrectly formatted string literals, it could be sufficient in certain scenarios. Nonetheless, creating a comprehensive Stringified<T> type for all T inputs would be challenging.

For cases where runtime stringifications result in the stringified values instead of hardcoded values in TypeScript code, a compile-time string parser may have limited usefulness.


If compile-time validation is not required, having Stringified<T> function as a nominal subtype of string dependent on T could be a suitable alternative. While TypeScript lacks direct nominal types, simulating them using structural typing and object intersection with the string type can achieve the desired effect.

type Stringified<T> = string & { __structure: T }

By introducing a phantom property __structure in Stringified<T>, the compiler enforces restrictions on assigning random strings to this type. At runtime, this property does not exist, making it a compiler-only feature.

Type assertions are required when assigning a string to such types. For instance, a stringify() function can handle this task to prevent haphazard assignments:

function stringify<T>(t: T): Stringified<T> {
    return JSON.stringify(t) as Stringified<T>; // assertion needed
}

Additionally, a parse() function can be implemented to convert a Stringified<T> back into a T. Although type safety isn't guaranteed due to the limitations of JSON.parse(), this approach enables stringified type tracking by the compiler.

function parse<T>(s: Stringified<T>): T {
    return JSON.parse(s); // type checks but not validated
}

These functions facilitate the parsing and validation of stringified values, ensuring the expected types are retained:

const foo = stringify({ a: 1, b: "two" });

const val = parse(foo);
console.log(val.b.toUpperCase()); // type-aware access

Attempts to parse a random string will be rejected, reinforcing the type safety of the implementation:

parse("oopsie"); // error!  Argument of type 'string' is not 
// assignable to parameter of type 'Stringified<unknown>'.

The proposed approach provides a robust solution for representing stringified objects in TypeScript, emphasizing type safety and clarity in handling stringified values.

Link to 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

React-table fails to show newly updated data

I am facing an issue with my react-table where real-time notifications received from an event-source are not being reflected in the table after data refresh. https://i.stack.imgur.com/q4vLL.png The first screenshot shows the initial data retrieval from th ...

How can a TypeScript Type be handed over as a prop to a React component?

Can you pass a TypeScript type as a property to a React Component? export type ActivitiesType = { RUN: "RUN"; WALK: "REST"; ROUNDS: "ROUNDS"; }; <MyComponent activity={ActivitiesType.RUN} /> Next, in MyComponent: const MyComponent = ({ act ...

Managing the state of dynamically generated tabs within a NextJS application

Looking to develop a web app in Next.js that includes tabs components. The goal is to manage various entities within each tab, such as utilizing a search bar to select different products. Upon selecting a product, a new tab will be generated with the produ ...

Transforming a JavaScript chained setter into TypeScript

I have been utilizing this idiom in JavaScript to facilitate the creation of chained setters. function bar() { let p = 0; function f() { } f.prop = function(d) { return !arguments.length ? p : (p = d, f); } return f; } ...

Dependencies exclusively for NPM post-installUnique Rewrite: "N

I have been using git to distribute an internal TypeScript NPM package. To avoid cluttering my repository with build files, I have implemented a postinstall action to build the package upon installation: "postinstall": "tsc -p tsconfig.json& ...

When executed, the Node application successfully compiles

I have a TypeScript application that runs smoothly in development mode using ts-node. However, after building the application, I encounter some unexpected warnings and errors. This is my tsconfig.json: { "compilerOptions": { "incremen ...

retrieve a nested object's property using a dynamic string

Here is the object model I am working with: export class FrcCapacity { constructor( public id?: number, public frcId?: number, public capGroupId?: number, public capGroup?: CapGroup, public salesProductId?: number, public p1?: num ...

Develop a customized configuration module for managing ESLint, Prettier, and resolving import issues efficiently

Currently, I am developing a configuration npm module for my personal project. This repository includes Prettier, ESLint, tsconfig, and other tools that I have set up. You can find my configuration tools repository here: https://github.com/Seyrinian/seyri ...

Tips on invoking Bootstrap's collapse function without using JQuery

We are facing a challenge with our TypeScript files as we have no access to jQuery from them. Our goal is to trigger Bootstrap's collapse method... $(object).collapse(method) but without relying on jQuery. Intended Outcome //Replicates the functio ...

The Alert dialog in Shadcn will automatically close upon clicking the trigger from the dropdown menu

It seems like other people have encountered this issue, but they all used the alert dialog in the same file. I attempted to open the alert dialog using "" and included a dropdownmenuitem with 'delete' inside it. However, when trying to open the ...

The term 'EmployeeContext' is being utilized as a namespace in this scenario, although it actually pertains to a type.ts(2702)

<EmployeeContext.Provider> value={addEmployee, DefaultData, sortedEmployees, deleteEmployee, updateEmployee} {props.children}; </EmployeeContext.Provider> I am currently facing an issue mentioned in the title. Could anyone lend a hand? ...

Typescript error: The value "X" cannot be assigned to this type, as the properties of "Y" are not compatible

Disclaimer: I am relatively new to Angular2 and typescript, so please bear with me for any errors. The Challenge: My current task involves subtracting a start date/time from an end date/time, using the result in a formula for my calculation displayed as " ...

Data can be retrieved in a React/Next.js application when a button is clicked, even if the button is located in a separate

Whenever the button is clicked, my function fetches weather data for the current location. I am trying to figure out how to transfer this data from the Location component to the pages/index.tsx. This is where another component will display the data. This ...

Error: Attempting to access the value property of a null object within a React Form is not possible

I am currently developing a form that includes an HTML input field allowing only numbers or letters to be entered. The abbreviated version of my state interface is outlined below: interface State { location: string; startDate: Date; } To initiali ...

Moving a custom folder to production on Vercel with Next.js

I am currently working on developing a project using the NextJS environment. In this project, I have some JSON files stored in a folder within my root directory, and I am reading the content of these files in my code. Everything works fine on my local mach ...

"Creating a sleek and efficient AI chess game using chess.js with Angular's

Cannot read property 'moves' of undefine Hello there! I am currently working on developing a chess game using Angular. I'm facing an issue with the artificial intelligence in the game where the piece seems to get stuck in the mouse. The l ...

Divs are not being organized into rows correctly due to issues with Bootstrap styling

I have implemented Bootstrap in my Angular application. The stylesheet link is included in my Index.html file as follows: <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.css"> In addition to that, I have listed Bootstrap a ...

Simulation of documentElement language property in Jest

I am currently attempting to manipulate the documentElement lang property for my testing purposes, but I am struggling to find a solution. I have tried defining setupFiles in Jest config, which allowed me to define it but did not give me the option to chan ...

Getting node siblings within an Angular Material nested tree: A comprehensive guide

Struggling to retrieve the list of sibling nodes for a specific Angular Material tree node within a nested tree structure. After exploring the Angular Material official documentation, particularly experimenting with the "Tree with nested nodes," I found t ...

Is Intellisense within HTML not available in SvelteKit when using TypeScript?

Having trouble with intellisense inside HTML for a simple page component. Also, renaming properties breaks the code instead of updating references. Typescript version: 4.8.4 Below is the code snippet: <script lang="ts"> import type { Bl ...