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

Struggling with getting render props to work in Next.js version 13

Looking to develop a custom component for Contentful's next 13 live preview feature in the app directory, I thought of creating a client component that can accept a data prop and ensure type safety by allowing a generic type to be passed down. Here is ...

What causes the updated value to be appended to an array when using the spread operator to modify an existing property's value?

In my state, I have an array of objects: const initialState=[{a:1},{b:2},{c:{d:3}}] const [state,setState]=useState(initialState) My goal is to modify the value of b to 5 within my event handler: function changeBToFive() { setState((state) => [ ...

Differences between tsconfig's `outDir` and esbuild's `outdir`Explanation of the variance in

Is there a distinction between tsconfig's outDir and esbuild's outdir? Both appear to accomplish the same task. Given that esbuild can detect the tsconfig, which option is recommended for use? This query pertains to a TypeScript library intended ...

Developing J2EE servlets with Angular for HTTP POST requests

I've exhausted my search on Google and tried numerous PHP workarounds to no avail. My issue lies in attempting to send POST parameters to a j2ee servlet, as the parameters are not being received at the servlet. Strangely though, I can successfully rec ...

The automatic inference of function argument types and the implementation of conditional types

I'm facing a specific scenario: There's a function that takes in a boolean and returns either a RealItem or an ImaginaryItem. I'm using conditional types to determine the return type based on the boolean argument. type RealItem = { color: s ...

unanticipated installation issue with published npm package on verdaccio

I am on a mission to create and release a package containing commonly used TypeScript functions on Verdaccio, an npm registry that operates on Docker. After completing the build for my TypeScript package, this is how my project structure looks: The impor ...

What is the best way to interweave my objects within this tree using recursion?

I am working on creating a new function called customAdd() that will build a nested tree structure like the one shown below: let obj = [] let obj1 = { key: "detail1Tests", id: "94d3d1a2c3d8c4e1d77011a7162a23576e7d8a30d6beeabfadcee5df0876bb0e" } ...

Enhance your coding experience with Angular Apollo Codegen providing intelligent suggestions for anonymous objects

Currently, I am exploring the integration of GraphQL with Angular. So far, I have been able to scaffold the schema successfully using the @graphql-codegen package. The services generated are functional in querying the database. However, I've noticed ...

Plugin for managing network connectivity in Ionic framework

In order to check if internet and id connection are available, I need to make a server request. I have implemented the Ionic Native Network Plugin following their official documentation. Here is my code snippet: import { Component } from '@angular/c ...

Troubleshooting problem with TypeScript observables in Angular 5

Having trouble with a messaging app, specifically an error related to TS. The syntax checker in the Editor is flagging this issue: Type 'Observable<{}>' is not compatible with type 'Observable'. Type '{}' cannot be assig ...

I am experiencing difficulties with TypeORM connecting to my Postgres database

Currently, I am utilizing Express, Postgres, and TypeORM for a small-scale website development project. However, I am encountering challenges when it comes to establishing a connection between TypeORM and my Postgres database. index.ts ( async ()=>{ ...

Determine the size of a BSON object before saving it to a MongoDB database using

I am currently in the process of determining the best way to calculate the size before storing certain data in MongoDB. After writing a script that parses and combines data into a single document, I have encountered an error when trying to use instance.sav ...

Is it impossible to access the length property of an undefined variable?

After developing a function that calculates the length of a string entered into an HTML textbox, I encountered an error when trying to display the result in another textbox. The function is designed to get the value from the 5th textbox on my HTML page and ...

Merging all Angular 2 project files into a single app.js document

I've scoured the depths of the internet for an answer to this burning question: How can I merge all my Angular 2 code, along with its dependencies, into a single file? Although this query has been posed countless times before, I bring a fresh perspect ...

Exploring TypeScript: Navigating the static methods within a generic class

I am trying to work with an abstract class in TypeScript, which includes an enum and a method: export enum SingularPluralForm { SINGULAR, PLURAL }; export abstract class Dog { // ... } Now, I have created a subclass that extends the abstract cla ...

Is there a way to verify the presence of multiple array indices in React with TypeScript?

const checkInstruction = (index) => { if(inputData.info[index].instruction){ return ( <Text ref={instructionContainerRef} dangerouslySetInnerHTML={{ __html: replaceTextLinks(inputData.info[index].instruction) ...

Are multiple click events needed for identical buttons?

In my component, there is an HTML structure like this: <div id="catalogo" class="container"> <div class="row"> <div *ngFor="let artista of artistas" class="col-sm" style="mar ...

Angular 10 Reactive Form - Controlling character limit in user input field

I'm currently developing an Angular 10 reactive form and I am looking for a way to restrict the maximum number of characters that a user can input into a specific field. Using the maxLength Validator doesn't prevent users from entering more chara ...

Setting the current date in Angular using TypeScript and storing it in a MySQL database

As I delve into learning Angular, I am focused on practicing with a form. Specifically, I am attempting to include the current date when inputting client records along with their RFC, branch, and cost. Unfortunately, my attempts have been unsuccessful in i ...

Display a child element depending on a certain condition

Looking to implement a switch higher-order component (HOC) for conditional rendering that mimics a switch statement in ReactTS. <Switch> <Case condition={cond1}> Hello </Case> <Case condition={cond2}> Wor ...