Implementing an extended interface as an argument in a function

Here is the code snippet for analysis:

interface IUserData {
    FirstName: string,
    LastName: string,
    Email: string,
    Password: string
}


interface IState extends IUserData {
    isSuccess: boolean
}

const state: IState = {
    FirstName: 'John',
    LastName: 'Doe',
    Email: 'email',
    Password: '1234',
    isSuccess: true
} 

const testFunction = (userData: IUserData): void => {
    console.log(userData);
}

testFunction(state)

The provided code outputs:

{
  FirstName: 'John',
  LastName: 'Doe',
  Email: 'email',
  Password: '1234',
  isSuccess: true
}

The question arises regarding why the function accepts the state object without throwing a type error. One would expect a compiler error indicating incorrect argument type.

To investigate further, an exploration was conducted to ascertain if it's possible to pass an object with only a subset of properties from the original object without creating a new object.

In summary, the expected output should be:

{
  FirstName: 'John',
  LastName: 'Doe',
  Email: 'email',
  Password: '1234',
}

Answer №1

It would be reasonable to assume that the compiler will generate an error indicating that the argument type is incorrect.

In fact, the argument type is accurate. The IState interface is a subtype of IUserData, meaning whenever you can use an IUserData, you can also use an IState. This behavior is standard in object type hierarchies, not only in TypeScript but also in languages like Java, C#, and others. (TypeScript does make one exception to this rule: when assigning a literal object to something [

let x: ABCType = {a: 1, b: 2, c: 3, d: 4};
where ABCType has only a, b, and c], or using an object literal as an argument in a function call, it warns about "excess properties" — not because it's wrong from a type perspective, but likely due to coding mistakes.)

Ultimately, the expected output should look like:

{
 FirstName: 'Jan',
 LastName: 'Kowalski',
 Email: 'email',
 Password: 'abc',
}

The logged value in your code is simply the parameter received by the function. No modification or extraction of properties occurs, so all properties of the object (including those from its subtype) are present.

TypeScript does not manipulate values; that's a runtime task, whereas TypeScript operates at compile time (except for minor runtime aspects like enums). If you wish to create a function that eliminates excess properties, you must handle that explicitly with runtime code; TypeScript won't handle it automatically. Unfortunately, generating property names from a type definition isn't feasible; instead, you need to derive a type definition from an existing model object during runtime.

Here's an example demonstrating this approach (not necessarily recommended, but illustrates how it can be done):

const sampleUserData = {
    FirstName: "Jan",
    LastName: "Kowalski",
    Email: "email",
    Password: "abc",
};
type IUserData = typeof sampleUserData;
const userDataKeys = new Set(Object.keys(sampleUserData));

const testFunction = (userData: IUserData): void => {
    // Filtering out non-IUserData properties
    const filtered = Object.fromEntries(
        Object.entries(userData).filter(([key]) => userDataKeys.has(key))
    ) as IUserData;
    console.log(filtered);
};

Subsequently:

interface IState extends IUserData {
    isSuccess: boolean
}

const state: IState = {
    FirstName: "Jan",
    LastName: "Kowalski",
    Email: "email",
    Password: "abc",
    isSuccess: true
};

testFunction(state);

outputs:

{
  "FirstName": "Jan",
  "LastName": "Kowalski",
  "Email": "email",
  "Password": "abc"
}

Playground link

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

Ways to avoid using a specific type in TypeScript

Imagine having a class that wraps around a value like this: class Data<T> { constructor(public val: T){} set(newVal: T) { this.val = newVal; } } const a = new Data('hello'); a.set('world'); // typeof a --> Primitiv ...

Verify whether the keys of Object1 exist in Object2; if they do, compare their corresponding values

How can I accomplish the following task? I want to check if the keys of object1 are present in object2. If a key is found, I need to compare its value with that of object2. If the values are different, I should replace the value in object2 with the one fro ...

How to convert an array of object values to an object with array values in a React application?

Here is an example of an array containing object values: { user: ["data1", "data2"], user2: ["data1", "data2"], } I am looking to convert this object value array into an object value array object, like so: { ...

In Typescript with Vue.JS, the type 'Users[]' does not include the essential properties of type 'ArrayConstructor' such as isArray, prototype, from, of, and [Symbol.species]

Embarking on my journey with typescript and vuejs, I stumbled upon a perplexing error that has halted my progress for the past 48 hours. The error message reads as: Type 'Users[]' is missing the following properties from type 'ArrayConstruct ...

Creating an observer for a multiple selection dropdown in Aurelia: step by step tutorial

In my current setup, I have a dropdown menu that allows users to select a single option. This selection automatically provides me with the filtering value as an observable. Here is how it works: public months: any=[]; @observable public selectedMonth: ...

The compatibility issue between Tailwind CSS and Material UI has been identified in the latest version of Next, version 14.0

Currently, I am working with Next 14.0.2 alongside Material UI and Tailwind CSS for my Frontend development tasks. However, I've run into some challenges regarding styling components. One specific issue I faced was when setting a button to navigate to ...

Encountering an XHR error when using a systemjs module in TypeScript

Error: GET http://localhost:63342/Dog.js 404 (Not Found) XHR error (404 Not Found) loading http://localhost:63342/Dog.js <br/><br/>Below is the script in my index.html file. ...

What is the best way to dynamically assign formControlNames in Angular using *ngFor?

I am currently facing a challenge with setting form controls using *ngFor over objects in an Array. The number of objects in the array can vary, sometimes resulting in only 1 object while other times multiple objects are present. My specific issue revolve ...

The default value in an Ionic select dropdown remains hidden until it is clicked for the first time

Having an issue with my ion-select in Ionic version 6. I have successfully pre-selected a value when the page loads, but it doesn't show up in the UI until after clicking the select (as shown in pic 2). I'm loading the data in the ionViewWillEnt ...

Display streaming data continuously within an HTML page using Angular 16

Currently, I am actively developing a stream API that receives data with a 'Content-Type' of 'text/event-stream'. Below is a snippet from my stream.service.ts: connectToSse(): Observable<any> { return new Observable((observer ...

Error message: The provider is not being recognized by react-redux while using NextJS with RTK and

Struggling to integrate Redux RTK into my Next JS 13.4 app has been quite the challenge. No matter how many tutorials I follow, I keep encountering the same error in my provider.ts file. 'use client' import { store } from './store'; imp ...

Can you explain the execution process of this Http.post method and provide details about the code path it follows

As I delve into the world of web development, one aspect that has me stumped is the functionality of the Http.post section within a project I stumbled upon on GitHub. Specifically, this pertains to an ExpressJS with Typescript repository I came across. So, ...

Tips for altering Koa's HTTP status code for undeclared paths

If an undefined route is accessed on a Koa server, what is the best method to change the default HTTP status code and response body? Currently, Koa returns a 404 status and 'Not Found' text in the body. I intend to modify this to 501 (Not implem ...

I am facing an issue with Angular reactive forms where the default values I set in ngOnInIt are not being reflected

Having some issues with setting default values in my Angular app using reactive forms. The defaults I set in ngOnInit are not showing up. I am also using the filter function within the map method. I am trying to select a value based on the URL and have it ...

Verifying data types in TypeScript

When working with TypeScript in the browser, I often find myself writing code like this: const button = document.getElementById(id); if (!(button instanceof HTMLButtonElement)) { throw new Error("TODO -- insert better error message here"); } bu ...

Error message: "Unable to find a windows instance" encountered while conducting tests on Paho MQTT Client using mocha and typescript

After spending countless days searching online, I have yet to find any resources on testing the Paho MQTT Client. My approach so far has been somewhat naive, as shown below: import { suite, test, slow, timeout, skip, only } from 'mocha-typescript&apo ...

Error: In Angular Firebase, the type 'string' cannot be assigned to the type 'Date'

I am encountering an error. The following error is shown: "cannot read property 'toDate' of undefined. Without the toDate() | Date." After further investigation, I found: A Timestamp object with seconds=1545109200 and nanoseconds=0. A hel ...

Using a union type annotation when passing into knex will result in the return of an unspecified

Knex version: 2.5.1 Database + version: postgres15 When passing a union typescript definition into knex as a type annotation, it returns the type any. However, by using type assertion as UserRecord, we can obtain the correct UserRecord type. It is my un ...

Using ThreeJS to Apply Dual Materials to a Mesh Entity

With ThreeJS, it's possible to incorporate more than one material into an Object3D/Mesh as stated in the documentation. You can either utilize a single Material or an array of Material: Class declaration and constructor for Mesh TypeScript file (exce ...

An angular component that is functioning properly when connected to a live server, however, it is experiencing issues when trying to run `

I tried integrating versitka into my Angular component by placing the version HTML file and all necessary assets in the appropriate directories. However, when I run ng serve, only the HTML file seems to be working, while the CSS and images fail to load. I ...