Implementing an interface by assigning a type interface to a class instance with additional properties

Just to clarify my question, let me provide more details. Let's say I have a class called MyClass that implements an interface named MyInterface. Besides the properties required for the implementation (such as myProp1), it also includes an additional property called myProp2, as illustrated in the code snippet below:

interface MyInterface {
  myProp1: string;
}

class MyClass implements MyInterface {
  myProp2 = '2';
  constructor(public myProp1: string) {}
}

Now, when I create an instance of this class and assign it the type of MyInterface, TypeScript does not raise any errors even though the instance object has properties that are not defined in the MyInterface:

let instance: MyInterface = new MyClass('1'); // no type error

However, if I try to access the extra property (myProp2) on this instance, an error occurs:

console.log(instance.myProp2); // 'myProp2' does not exist on type 'MyInterface'. Did you mean 'myProp1'?

Should I consider this behavior a bug, or is it actually intended by design?

Answer №1

Everything is functioning as expected. TypeScript utilizes structural subtyping, where a value of type X can be assigned to a variable of type Y if every required member in type Y is present in type X.

In order for a value to meet the criteria of being a valid MyInterface, it must simply possess a myProp1 property of type string. Since every instance of the class MyClass includes this property, TypeScript allows you to assign a MyClass instance to a variable of type MyInterface without any errors.

It's worth noting that while TypeScript may highlight extra properties through excess property checking, warnings only occur in specific situations, typically when an object literal contains more properties than anticipated. As the expression new MyClass('1') isn't an object literal, no warning is triggered, as documented in microsoft/TypeScript#5303.


Furthermore, the compiler doesn't monitor assignments made to variables of non-union types. The type of instance is exclusively the non-union type

MyInterface</code, so the assignment <code>instance = new MyClass('1')
does not alter this. Consequently, there is no narrowing down to MyClass, meaning you cannot access the myProp2 property via instance since MyInterface lacks knowledge of such a property.

This contrasts the behavior with union-typed variables whereby variables are narrowed after assignments to compatible union members:

let instance: MyClass | Date = new MyClass('1');
instance.myProp2; // permissible

This implies that it's preferable not to specify a variable's type with a non-union type unless intending for the variable to accommodate various values of that type without necessitating tracking. If instance will consistently represent an instance of MyClass and that's all that matters, specifying it as follows suffices:

let instance: MyClass = new MyClass('1');
instance.myProp2; // permissible

Alternatively, refrain from providing any annotations and allow the compiler to determine its type as MyClass:

let instance = new MyClass('1');
instance.myProp2; // permissible

As a general rule of thumb, opt to let the compiler infer types whenever possible, reserving manual annotations for scenarios requiring greater control than inference provides.

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

Tips for converting Javascript require to Typescript import using the const keyword

Recently, I've been attempting to import faktory_worker_node from github.com/jbielick/faktory_worker. The README provides the following instructions: const faktory = require('faktory-worker'); faktory.register('ResizeImage', asyn ...

Reversing ngModel modifications does not accurately display changes in the view

Presently, my table contains editable cells, with the functionality to undo changes to each cell. To achieve this, I initially created a duplicate of each object in the array. Upon initialization, I mapped the array to create a new array with old values s ...

Cypress with Typescript: Best ways to retrieve specific values from a dataTable

I am facing an issue with a data table that contains a list of states and messages which I need to validate in a particular scenario. Given user inputs "<state>" and sees "<message>" message | state | message | | Deac ...

My worker threads seem to be flying under the radar

Currently, I am working on implementing worker threads into my Node.js/Typescript application. I have made significant progress, but it appears that my worker threads are not being executed as expected. Despite adding loggers inside the function intended f ...

Accessing app state in angular guard using NGRX - What is the procedure?

In my route guard(canActivate), I am looking to access the current state of my app in order to validate data and prevent users from navigating incorrectly. How can I retrieve the current state? I attempted using a subscription on the store to obtain the s ...

What is the proper method for specifying the path to my index.tsx file within a React application?

After using npx create-react-app my-app --template typescript to generate my React app, I decided to reorganize the files by moving them to /src/client and relocating my Express backend to /src/server. However, upon running the relocated React app, I encou ...

Error in TypeScript on SendGrid API: Invalid HttpMethod

Here is my code snippet: import sendgridClient from '@sendgrid/client' sendgridClient.setApiKey(process.env.SENDGRID_API_KEY); const sendgridRequest = { method: 'PUT', url: '/v3/marketing/contacts', bo ...

Preventing Button Clicks in Angular 2 When Form Data is Modified

I am looking to determine if at least one field in my form has been altered. When this condition is met, the disableButton variable will be set to true, and false if there are no changes in the form. Here is the snippet of code I am currently using: // Th ...

Why is it not possible to return a header in a Typescript function?

I am new to using typescript and I have encountered an issue with a value for headers. Initially, it worked fine when directly set in the code. However, when I attempted to move it into a separate function that could be called, the functionality broke. Be ...

Angular HTTP post is failing on the first attempt but succeeds on the second try

Just started working on my first Angular exercise and encountered an issue where I am receiving an undefined value on the first attempt from an HTTP post request. However, on the second try, I am getting the proper response in Edge and Firefox. Thank you f ...

What's the best way to update the outcome of a listener function that runs continuously?

I'm facing an issue with a function that consumes a Queue from RabbitMQ. I need to save the result every time it runs in order to perform additional operations (e.g. saving data in a database). The problem lies in the fact that the result is only sav ...

Ways to narrow down const types

For the given scenario, I aim to enforce a specific format [string, number] for all function returns, while allowing varying input arguments for these functions. Access the Playground here type ReturnFormat = [string, number] type Fn = (...args: any[]) =& ...

Angular 4 allows you to assign unique colors to each row index in an HTML table

My goal is to dynamically change the colors of selected rows every time a button outside the table is clicked. I am currently utilizing the latest version of Angular. While I am familiar with setting row colors using CSS, I am uncertain about how to manip ...

Is it possible to pass an external function to the RxJs subscribe function?

Upon examining the RxJS subscribe method, I noticed that: subscribe(next?: (value: T) => void, error?: (error: any) => void, complete?: () => void): Subscription; So, I decided to create an example initialization function like this: private ...

Difficulties with managing button events in a Vue project

Just getting started with Vue and I'm trying to set up a simple callback function for button clicks. The callback is working, but the name of the button that was clicked keeps showing as "undefined." Here's my HTML code: <button class="w ...

Error Alert: The function split cannot be applied to params[item]

ERROR! The console is outputting a message stating that TypeError: params[item].split is not a function. I would greatly appreciate any assistance in resolving this issue. Understanding where I went wrong in tackling this problem would be the most benefici ...

The inner map function isn't being executed in Rxjs

I have written some code that utilizes two pipes to load a product instance along with its playlist. In case there is an error while loading the playlist, the property is set to null: getProduct(productId: number): Observable<ProductDTO> { retur ...

Is it possible to make the 'keyof' property optional?

Illustrate an interface in the following way interface Properties { apple?: string banana?: string cherry?: string date: string } Executing this code works as expected type Sample1 = { [P in keyof Properties]: Properties[P] } const s1: Sample1 ...

Protractor experiencing difficulty recognizing Angular functionality

Recently, I made the switch to using Protractor for running end-to-end tests on my Angular application. However, the e2e tests have suddenly started failing because Protractor is unable to detect Angular on the website. I raised this issue in their GitHub ...

The TypeScript compiler throws an error when encountering nulls in conjunction with the isNull function

Whenever I set strictNullChecks: true in tsconfig.json and utilize the isNull function for null checks, the compiler throws the error TS2531: Object is possibly 'null'. Interestingly, isNull doesn't trigger any errors in VsCode, however, the ...