What is the process for defining a property type as a textual representation of a Type name in TypeScript?

Consider the following classes and interface:

class Foo {}
interface Bar {}

I am looking to define a type that includes a property with a specified type:

type DynamicPropertyName<T> = ... <-- ???

After defining the type, I want to use it like this:

type WithPropFoo = DynamicPropertyName<Foo>; // expected: {Foo: any}
type WithPropBar = DynamicPropertyName<Bar>; // expected: {Bar: any}

Instead of using any, I can specify my desired type easily.

Update after the third comment

Is there a way to achieve something similar to this:

type WithPropFoo = DynamicPropertyName<Foo>; // expected: {type: 'Foo'}
type WithPropBar = DynamicPropertyName<Bar>; // expected: {type: 'Bar'}

const foo: WithPropFoo = {type: 'Foo'};   // should be valid
const foo: WithPropFoo = {type: 'Hello'}; // should result in an error

const bar: WithPropBar = {type: 'Bar'};   // should be valid
const bar: WithPropBar= {type: 'Hello'};  // should result in an error

Answer №1

Unfortunately, achieving this in TypeScript is not feasible.


The concept of interface names like Bar being unobservable poses a challenge. While it may be difficult to find a definitive source on this, references like this comment on Microsoft/TypeScript#3060 and possibly this comment on Microsoft/TypeScript#3628 provide related insights and rationales.

It's essential to grasp that TypeScript's type system is structural rather than nominal. Essentially, if two types A and B exhibit the same structure, they are considered the same type, regardless of their names or declarations. For instance:

interface A {x: string}
interface B {x: string}
const c = {x: "hello"};

function acceptA(a: A) {}
acceptA(c); // valid

function acceptB(b: B) {
  acceptA(b); // valid
}    

In this scenario, A and B possess distinct declaration sites and names, while c's type is inferred as an anonymous type. Yet, passing c into acceptA() or any arbitrary value of type B</code into <code>acceptA() doesn't perturb the compiler, treating A, B, and typeof C as identical types. Despite possible differences in display, they represent the same type underneath.

Therefore, creating any type function type F<T> = ... wherein F<A>, F<B>, and F<typeof c> yield distinct types fundamentally contradicts the principles of structural typing and is unattainable.

While there are exceptions where the compiler deviates from structural typing, such occurrences are rare and unreliable. It's akin to coaxing out internal details by exploiting edge cases, offering undefined results instead of actual solutions.


Class names differ slightly due to potential runtime access to the name property for class constructors. However, relying on this property having a specific value at runtime isn't advisable.

An inquiry in microsoft/TypeScript#43325 regarding more strictly typed name properties within classes suggests possibilities for achieving this with class Foo.

Even if runtime assessments of the name property were guaranteed, TypeScript discourages exposing this feature at the type level to prevent conflicts arising from subtypes inheriting specific characteristics like static name properties.


In essence, the compiler does not distinguish between Foo labeled as Foo and Bar named as Bar. To align effectively with TypeScript, replicating this indifference would be prudent. Desiring otherwise might indicate encountering an XY problem, necessitating reassessment of your core objectives.

If you seek representation of string literal types like "Foo" and

"Bar"</code, preemptively declare them in your code:</p>
<pre><code>class Foo {
  fooProp = 123
  readonly myName = "Foo"
}

interface Bar {
  barProp: string;
  myName: "Bar";
}

This explicit inclusion of strongly-typed names under a myName property enables accessing these identifiers:

type DynamicPropertyName<T extends { myName: string }> = 
  { [K in T["myName"]]: any };

type WithPropFoo = DynamicPropertyName<Foo>; // {Foo: any}
type WithPropBar = DynamicPropertyName<Bar>; // {Bar: any}

Though seemingly repetitive, structural typing means this approach avoids redundancy or ambiguity. The name assigned to types like Bar holds no relevance, making constructs like

interface Qux {barProp: string myName: "Bar"}
indistinguishable from Bar concerning functional aspect.

If this method fails to address your needs, exploring alternative strategies becomes imperative. Best of luck!

Link to Playground showcasing this approach

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

The disappearance of the "Event" Twitter Widget in the HTML inspector occurs when customized styles are applied

Currently, I am customizing the default Twitter widget that can be embedded on a website. While successfully injecting styles and making it work perfectly, I recently discovered that after injecting my styles, clicking on a Tweet no longer opens it in a ne ...

Enhancements to managing universal configuration object across the entire application

My current project involves working on an application with multiple products. To streamline product-specific configuration and eliminate the need for excessive if-else statements, I am looking to implement product-specific config keys that are consistently ...

Does the term 'alias' hold a special significance in programming?

Utilizing Angular 2 and Typescript, I have a component with a property defined as follows: alias: string; Attempting to bind this property to an input tag in my template like so: <input class="form-control" type="text" required ...

Setting up WebPack for TypeScript with import functionality

A tutorial on webpack configuration for typescript typically demonstrates the following: const path = require('path'); module.exports = { ... } Is it more advantageous to utilize ES modules and configure it with import statements instead? Or is ...

Exploring the Power of Nested *ngFor in Angular 2

I am struggling with passing indexes to a function where the first parameter (ii) is coming back as undefined. <div *ngFor="let tab of screen.data.tabs; let indexTab = i;"> <div *ngIf="tab.active"> <div *ngIf="tab.questions"&g ...

Discovering the import path of Node modules in ReactAlgorithm for determining the import path of

Software Development In my current project, I am utilizing Typescript along with React. To enhance the application, I integrated react-bootstrap-date-picker by executing yarn install react-bootstrap-date-picker. Unfortunately, there is no clear instruct ...

Is there a way to simulate Pinia and vue-i18n simultaneously?

Exploring Vue 3's Composition API with a twist: The store.ts file import { ref, Ref } from 'vue'; import { defineStore } from 'pinia'; export const useStore = defineStore('store', () => { const isLoading: Ref<bo ...

Error message: "The property is not found within the specified type when using the OR operator with

Within my Angular component, I am faced with a challenge involving an Input that can be one of two types. @Input() profile: UserProfileDetails | BusinessProfileDetails; The structure of the profile template is straightforward and I want to avoid duplicati ...

The 'filter' attribute is not found in the 'ContextProps' type

I am currently working on a project in Next.js 13 where I am trying to render card items conditionally based on their status. The TypeScript version being used is "5.2.2". However, I encountered an error that says: Property 'filter' does not exis ...

Using Selenium WebDriver with JavaScript: Handling Mouse Events (mouseover, click, keyup) in Selenium WebDriver

I am currently working on integrating Selenium testing for mouse events with dynamically generated elements. Specifically, I am attempting to trigger a "mouseover" event on an element and then interact with some icons within it. However, I have encountere ...

Is React Typescript compatible with Internet Explorer 11?

As I have multiple React applications functioning in Internet Explorer 11 (with polyfills), my intention is to incorporate TypeScript into my upcoming projects. Following the same technologies and concepts from my previous apps, I built my first one using ...

The error message "TypeError: 'results.length' is not an object" occurred within the Search Component during evaluation

Having trouble with a search feature that is supposed to display results from /api/nextSearch.tsx. However, whenever I input a query into the search box, I keep getting the error message TypeError: undefined is not an object (evaluating 'results.lengt ...

Manipulate values within an array when a checkbox is selected or deselected

I am developing an Angular application where I have created a checkbox that captures the change event and adds the checked value to an array. The issue I am facing is that even if the checkbox is unchecked, the object is still being added to the array. D ...

Using Express middleware in a TypeScript Express application

I'm currently converting the backend of an ExpressJS application to Typescript. While working on the auth.routes.ts file, I encountered an issue with the middleware (authMiddleware). It seems like there might be a typing error, as the same code in the ...

Unable to locate module: Issue: Unable to locate '@angular/cdk/tree' or '@angular/material/tree'

Currently utilizing Angular 5 and attempting to create a tree view that resembles a table layout. https://stackblitz.com/edit/angular-hhkrr1?file=main.ts Encountering errors while trying to import: import {NestedTreeControl} from '@angular/cdk/tree ...

Experiencing difficulties with a click event function for displaying or hiding content

Struggling with implementing an onClick function for my two dynamically created components. Currently, when I click on any index in the first component, all content is displayed. What I want is to show only the corresponding index in the second component ...

A guide to submitting comments with Angular 2 using a GitHub OAuth token

I'm attempting to utilize a GitHub OAuth Token for posting comments in Angular 2. Below is the code I am using: postComment(token: string, number: Number, body: string): Promise<Comment> { let headers = new Headers() headers.append('Auth ...

The Bootstrap modal I implemented is opening correctly, but for some reason, the form inside is not appearing

I created the AddJokeModalComponent to streamline the process of opening a form without duplicating code in every component. Below is the modal structure: <ng-template #addJokeModal> <div class="modal-content my-custom-modal"> ...

SvgIcon is not a recognized element within the JSX syntax

Encountering a frustrating TypeScript error in an Electron React App, using MUI and MUI Icons. Although it's not halting the build process, I'm determined to resolve it as it's causing issues with defining props for icons. In a previous pro ...

An exploration of effortlessly moving elements using webdriver.io - the power of

I have been attempting to utilize the drag and drop method in WebDriver.io, but I am encountering issues. I followed the example for drag & drop on this website: https://www.w3schools.com/html/html5_draganddrop.asp. This functionality is essential for ...