The TypeScript compiler does not allow a 'number' type to be assigned to 0, 10, or 20, even when the number itself is 0

When testing out my code snippet on the playground for Typescript, an error appears on line 24.

I discovered that the issue can be resolved by explicitly casting commands back to <IPlan[]>, but I wonder why this extra step is necessary.

Property 'commands' in type 'Thing' cannot be assigned to the same property in base type 'AbstractThing'.
  Type '{ plan: number; }[]' cannot be assigned to type 'IPlan[]'.
    Type '{ plan: number; }' cannot be assigned to type 'IPlan'.
      The types of the 'plan' property are incompatible.
        Type 'number' cannot be assigned to type '0 | 10 | 20'.
(property) Thing.commands: { plan: number; }[]

Below is the code snippet:

interface IPlan {
  plan: 0 | 10 | 20;
}

// This works
let obj: IPlan = {
  plan: 0
};

// Also works
let commands: IPlan[] = [
  {
    plan: 10
  }
];

// Does not work
abstract class AbstractThing {
  commands?: IPlan[];
}

class Thing extends AbstractThing {
  // The error occurs on this line
  commands = [
    {
      plan: 10
    }
  ]
}

Answer №1

An identified issue exists where properties and methods are not contextually typed based on the properties of a base class or implemented interface until after normal type inference occurs. This leads to errors in code implementations that should match these constraints.

class Foo {
  x = "hello";
}

interface Interface {
  x: "hello" | "goodbye";
}

class Bar implements Interface {
  x = "hello"; // error!
}

class Base {
  x: "hello" | "goodbye" = "hello";
}
class Baz extends Base {
  x = "hello"; // error!
}

The property x is inferred as a widened string type, causing conflicts with interfaces or classes it should adhere to. Despite previous attempts to resolve this issue, it persists due to complications arising from proposed changes.

To work around this, explicit type annotations can be used to align properties with expected types:

class Bar implements Interface {
  x: Interface['x'] = "hello"; // okay, "hello" | "goodbye"
}
class Baz extends Base {
  readonly x = "hello"; // okay, readonly narrows to "hello" only
}

These annotations help the compiler understand and enforce correct property typings, providing a temporary solution to the problem until a more permanent fix is implemented.

Hopefully these suggestions prove helpful in addressing the issue at hand. Best of luck!

Answer №2

It is strange how using the abstract class can cause it to interpret the definition of plan as number rather than the specific values you provided.

To ensure the correct type is utilized, consider implementing one of these "workarounds":

class Thing extends AbstractThing {
  commands = [
    {
      plan: 10
    }
  ] as IPlan[]
}

or

class Thing extends AbstractThing {
  commands: IPlan[] = [
    {
      plan: 10
    }
  ]
}

Answer №3

According to my analysis, TypeScript might struggle with inferring the type if the variable is declared outside of a method.

To resolve this issue, simply relocate the declaration inside the constructor, as illustrated here:

class Thing extends AbstractThing {
  constructor() {
    super();
    this.commands = [
      {
        plan: 10
      }
    ];
  }
}

This issue could potentially be just a glitch in the system.

Answer №4

In the definition of your interface, you limit the type of the plan property to be either 0, 10, or 20. However, when you assign the commands property within the class:

commands = [
  {
    plan: 10
  }
]

The plan property is assigned a value of number. Despite setting it to 10, there is no constraint on the type itself, making it less strict than the defined type in the interface, leading to a compiler error.

To resolve this issue, one simple solution is to cast the object to the interface type:

commands = [
  (<IPlan>{
    plan: 10
  })
]

When dealing with types, think of them as sets. If you were the type-checker program and encountered the literal expression

{
  plan: 10,
}

You could interpret this in multiple ways. Here are two possibilities for our discussion:

  • An object with a plan attribute where the only accepted value is 10.
  • An object with a plan attribute that can hold any number.

There are additional complexities to consider (like null values or other keys), but the compiler may struggle to infer compatibility solely based on using the literal '10'. Additionally, the value might change dynamically at runtime, adding to the uncertainty.

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

Expand the capabilities of a jQuery plugin by incorporating new functions or modifying existing ones

I have a task to enhance the functionality of a jQuery Plugin (https://github.com/idiot/unslider) by adding another public method. (function(){ // Store a reference to the original remove method. var originalMethod = $.fn.unslider; // Define a ...

What steps should I take to verify the validity of an Angular form?

I am struggling with validating an inscription form in HTML. Despite trying to implement validations, the inputs are still being saved in the database even when they are empty. Here are the validations I want to include: All inputs are required Email addr ...

The use of JSON.parse does not support parsing URL links

As a junior developer specializing in Javascript and Google Apps Script, I decided to enhance the functionality of my Google Sheets by tracking the last modification time of URLs stored in them. Although I attempted to create a script for this task, it see ...

Utilizing middleware with express in the proper manner

Hello there! I'm just double-checking to see if I am using the correct method for implementing middleware in my simple express app. Specifically, I am trying to ensure that the email entered during registration is unique. Here's an example of wha ...

Activate Bootstrap button functionality with JavaScript

Hey team, I've got a Bootstrap button on my HTML page: ..... <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> ... <div class="accept-btn"> <button type="button" class="bt ...

Exploring ways to utilize array.find by comparing it to a different object

There are two arrays in JavaScript named designs and articles. var designs = [ { design_id:"bwbmbqlujurv", article_id:14782, name:"adidas demogorgon black" }, { design_id:"lg9yba2gkcwr", article_id:14782, name:"harry potter w ...

JavaScript for Altering Viewport/Screen Width

Lately, I've been experimenting with creating a responsive button for our new website. The goal is to adjust the body width to 460px, simulating how our site would appear on mobile devices. While researching, I came across a technique that utilizes if ...

Ways to identify the visible elements on a webpage using JavaScript

I'm working on a nextjs app , I am looking to dynamically update the active button in the navbar based on which section is currently visible on the page. The child elements of the page are structured like this: <div id="section1" > < ...

What is the best way for a client to showcase a constantly fluctuating server-side variable's value?

On my website's homepage (index.html), I want to show a dynamic server-side global variable called serverVariable. This variable is always changing based on the node.js server-side code. However, since the client doesn't know what serverVariable ...

Delete the click event listener that was previously assigned by a different script

After extensive investigation using the Chrome developer tools, I have identified a click event listener that needs to be removed: https://i.sstatic.net/dnB3Q.png I successfully removed the listener using the developer tools. Further analysis revealed th ...

What is causing the onclick event to not function properly when called from an external .js file?

I've created a .js file that contains code for a photo album application. The file includes functions for changing images when buttons are clicked. However, when I interact with the buttons, the images do not change as expected. The code in the .js f ...

modifying output of data when onchange event is triggered

I am struggling with creating an onchange event for my option box in which the users of a site are listed. I have two input boxes designated for wins and losses, but the output is not changing when I select a user from the list. What could be wrong with my ...

Two ng-click events are triggered within a nested ng-click

I am facing an issue with using the ng-click within a nested list under another list that also has a ng-click event. Here is my code snippet: <ul> <li ng-repeat="facet in node.Facets" ng-click="updateNav(facet.Action)" ng-cloak & ...

Navigating through each segment

I'm currently working on a website with sections that are set to be 100% height of the window, but at least 800px tall. My goal is to implement a smooth scrolling functionality where one scroll moves the view from section to section. However, if the ...

Automated service worker upgrade procedure

Currently, I have some concerns regarding the update process of the service worker used in my project. Within this project, there are two key files associated with the service worker: The first file, "sw.js", is located in the root of the website and is i ...

The HttpParams are reluctant to be established

Working with Angular 8, I am attempting to assign an HttpParam using the provided code snippet and observing the outcome on the final line. let newParams = new HttpParams(); newParams.set('ordering', 'name'); console.log('getting: ...

Can you explain the distinction between object allocation using the '=&' operator and the Object.create() method?

I have been delving deep into JavaScript object operations. My question revolves around the difference between const me = Object.create(person); and const me = person;. Both of these operations provide a similar output as they reference the object to a new ...

mandating the selection of checkboxes

Currently, I am exploring the possibility of automatically selecting a checkbox when an option is chosen from a dropdown menu. Below is a code snippet that demonstrates what I am aiming to tweak: $('.stackoverflow').on('change', func ...

Error in React Native: The function this.setState is not defined. (The function "this.setState" is not a valid function)

I am working on integrating JSON data from an open source API and storing it as a state. However, I am facing challenges in saving the JSON data into the state which I want to use for city matching. It would be really helpful if someone could provide assis ...

What is the best way to bring up the keyboard on an iPhone when focusing an HTML text field?

Currently working on developing a web app for iPhone, and looking to implement a feature where a text field is automatically focused upon page load, prompting the keyboard to appear. Despite attempting the standard Javascript method: input.focus(); I see ...