What is the best approach to incorporating a series of values in TypeScript through an interface?

Trying to incorporate an interface in a class like this:

type TLanguage = "TYPE" | "SCRIPT" // Values that can be reused

interface AnyInterface {
    business: TLanguage
    /** MoreTypes */
}

class Anyclass implements AnyInterface{
    business = "TYPE";
}

An error is being thrown:

Property 'business' in type 'Anyclass' cannot be assigned to the same property in base type 'AnyInterface'. Type 'string' cannot be assigned to type 'TLanguage'.ts(2416)

The expected behavior should throw an error whenever any value outside of the TLanguage scope is set for business... It does work when it's set as a variable:

const anyConst: AnyInterface = {
    business: "TYPE" //✅
    business: "TYPEs" //❌ Type '"TYPEs"' cannot be assigned to type 'TLanguage'. Did you mean '"TYPE"'?ts(2820)
}

Answer №1

When a class that implements an interface, the type of the class remains unaffected. While the compiler ensures that the class is compatible with the interface, it does not utilize the interface as a context to provide types to the class members. This lack of contextual inference can be frustrating for TypeScript users who expect otherwise. For more information, refer to microsoft/TypeScript#32082 and related issues.

When you declare

class Anyclass implements AnyInterface {...}
, the behavior of the compiler is similar to if you had only written class Anyclass {...} without the implements AnyInterface. Incorrect implementation (or when inferred by the compiler) will result in an error associated with the name Anyclass.

This leads to the issue where

class Anyclass { business = "TYPE" }
automatically widens business from the literal type "TYPE" to string due to the absence of context within the compiler, and adding implements AnyInterface does not alter this behavior.


Consequently, solutions for this problem are identical regardless of whether you include or omit implements AnyInterface. You could explicitly specify the type of the field:

class Anyclass implements AnyInterface {
    business: TLanguage = "TYPE"
}

Alternatively, you could use a const assertion to prevent the widening of the string literal (although this would infer "TYPE" instead of TLanguage for the property type):

class Anyclass implements AnyInterface {
    business = "TYPE" as const
    // (property) Anyclass2.business: "TYPE"
}

Another approach is to make it a readonly property, which also indicates to the compiler not to widen the type (keeping it as "TYPE") while preventing reassignment:

class Anyclass implements AnyInterface {
    readonly business = "TYPE"
    // (property) Anyclass3.business: "TYPE"
}

Access the code on Playground

Answer №2

To address this issue, simply use as const:

type TCategory = "CATEGORY_A" | "CATEGORY_B"

interface SomeInterface {
    type: TCategory
    /** AdditionalInfo */
}

class SomeClass implements SomeInterface{
    type = "CATEGORY_A" as const
}

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 is the best way to transfer data from a custom method and use it in another method within my constructor function in Javascript?

As I pondered a suitable example, I devised a constructor function centered around precious metals. This function accepts the type of metal and its weight as parameters. Within this constructor, there are two methods: one to verify if the precious metal (g ...

Save user sessions in a database using node.js, express, and mongoose-auth

I have a question about authentication and sessions in node.js. So, I've set up authentication using express.js and mongoose-auth with mongodb: app.use(express.cookieParser()); app.use(express.session({ secret: 'esoognom'})); app.use(auth. ...

Increase the totalAmount by adding the product each time

Can someone help me understand why the totalAmount shows as 20 when I add a product? Also, why doesn't it increase when I try to increment it? Any insights would be appreciated. Thank you. ts.file productList = [ { id: 1, name: 'Louis ...

Although AJAX $.post functions properly in the View, it seems to encounter issues when relocated to a separate .js file. Interestingly, all other JQuery functions work

I have recently delved into MVC, JQuery, and AJAX, and encountered a perplexing issue. After completing the initial development of a practice website, I dedicated time to enhance the interactivity using JQuery. Everything was functioning smoothly until I ...

Vue has issued a warning stating that the type check for the "eventKey" prop has failed. The expected type was a String or Number, but an Array was provided instead. Additionally, it is advised to

The code I am currently using is producing the following errors in the console output: [Vue warn]: Avoid using non-primitive value as key, use string/number value instead. [Vue warn]: Invalid prop: type check failed for prop "eventKey". Expected String, ...

Creating a Modal using Typescript with NextJS

Currently, I'm working on creating a modal within my app using NextJS with Typescript. Unfortunately, I've been struggling to eliminate the warning associated with my modal selector. Can someone provide guidance on how to properly type this? cons ...

Guide on integrating animate.css animations with Vue's Transition and TransitionGroup components

Vue offers the v-if and v-for directives that allow you to manipulate elements in the DOM based on certain conditions. In order to animate elements controlled by v-if and v-for, you need to utilize the built-in Transition and TransitionGroup components. Bu ...

React's connect method is causing issues with my test case

Attempting to create a test case for my jsx file... Took a sample test case from another jsx file... The other file does not have the connect method... But this new file contains the connect method... Believe this is causing issues with my test case... Any ...

updating information automatically on page every X seconds for Angular component

I am trying to implement a way to automatically refresh the data of an Angular component every 30 seconds. Currently, I have used a simple setInterval function like this: this.interval = setInterval(() => { this.refresh(); // api call ...

Interactive AJAX div click functionality

For this code to work, the user needs to first click the like button and then continue to proceed. However, I am having trouble getting the div event to function properly. The like button is located within a div called postos. When something is clicked wit ...

What is the best way to implement the Snackbar functionality within a class-based component?

My snackbar codes are not working as expected when I click the "confirm" button. I want the snackbar to appear after clicking the button. Most examples I've seen use functional components, so how can I get the Snackbar to work properly in a class comp ...

Click to load an IFRAME upon clicking

I am encountering an issue with IFRAMEs loading onClick. The problem lies in the script provided below which always loads the content of the first iframe, whereas my expectation is to load only the iframe corresponding to the link clicked. $('.toggle ...

Making all requests server-side in Next.JS: A step-by-step guide

I am in the process of creating a Next.JS application that will be retrieving data from both a Python API and a Postgres Database. Although this task may seem straightforward, the project requirements dictate that all requests must originate from the serv ...

Display conceal class following successful ajax response

Upon clicking the button, the following script is executed: $.ajax({ url: "<?php echo CHILD_URL; ?>/takeaway-orders.php", type: 'POST', async:false, data: 'uniq='+encodeURIComponent(uniq)+'&menu_id=' ...

In the world of web development with JavaScript, jQuery, and EasyUI, we often encounter situations where the parameter

function formatData_original() { // convert obj_num2.formatter = function(value, rec) { var baseStr='&nbsp;&nbsp;' + rec.s_date + '<a class="easyui-linkbutton" href="javascript:void(0);" plain= ...

Error: Missing default export in the imported module "react" according to ESLint

Query import React, { useContext, useEffect, useRef } from 'react'; After enabling esModuleInterop and allowSyntheticDefaultImports in tsconfig.json, using eslint-import-plugin and eslint-import-resolver-typescript for import linting triggers an ...

what is the best way to create a wishlist feature using React.js

I'm working on creating a wishlist feature using React.js. My goal is to add an item to the wishlist when the user clicks the wish button, and mark it as liked by setting the boolean data "liked: true". Additionally, I want the item to be removed fr ...

Typescript type definitions - understanding inheritance

My typescript interface defines the structure of my database data as follows: interface Foo { bar: { fish: { _id: string, name: string, }[], }, starwars: string[], } I want to be able to reference specific parts of this inter ...

I need help setting up the right configuration for a custom directory of Nuxt.js components. What should

<h1>Greetings everyone</h1> Currently, I'm in the process of learning how to utilize nuxt.js and have an inquiry regarding the utilization of custom directories that differ from the standard structure of a nuxt.js application. The proble ...

Creating a Unique Flot Bar Chart Experience

I am currently working on creating a bar chart and have the following requirements: Bar labels for each data point Centering the bars in the grid Below is my jQuery code snippet: var data = [[0,206],[1,118],[2,37]]; var dataset = [ { labe ...