A function's behavior will vary depending on whether it is a property of an object or not

I've been observing some peculiar behavior:

In a typical scenario, TypeScript usually raises an error when an object contains too many keys, like this:

type Foo = {
  a: string; 
}

const a: Foo = {
  a: "hello", 
  b: "foo" // Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.(2322)
}; 

function returnsFoo() : Foo {
  return {
    a: 'hello', 
    b: "world" // Object literal may only specify known properties, and 'b' does not exist in type 'Foo'.(2322)
  }
}

However, I noticed that if instead of directly declaring a function, I define a type that includes a function as a property, this error mysteriously disappears:

type ObjectThatHasAFunctionThatReturnsFoo = {
  defaultData: () => Foo; 
}; 

const b: ObjectThatHasAFunctionThatReturnsFoo = {
  defaultData: ()=> ({
    a: "hello", 
    //@ts-expect-error - but no error. 
    b: "asa"
  })
}

What could be the reason behind this unexpected behavior?

Answer №1

For more information, please refer to the documentation

When assigning object literals to variables or passing them as arguments, excess property checking occurs for special treatment. You will encounter an error if the object literal contains properties that are not present in the "target type":

To avoid these checks, you can assign the object to another variable. This way, the compiler will not flag it as an error.

It seems like there is no error in your code:

type Foo = {
    a: string;
}

const a: Foo = {
    a: "hello",
    b: "foo" 
};

function returnsFoo(): Foo {
    return {
        a: 'hello',
        b: "world" 
    }
}

type ObjectThatHasAFunctionThatReturnsFoo = {
    defaultData: () => Foo;
};


const b: ObjectThatHasAFunctionThatReturnsFoo = {
    defaultData: () => ({
        a: "hello",
        b: "foo"
    })
}

Here

This is because the function foo is essentially assigned to the defaultData property.

Since you are neither assigning the object literal to another variable nor using it as an argument, there are no excess property checks performed

That's right - the extra property 'b' indicates a developer mistake

To address this issue, you may consider incorporating some validation:

type Foo = {
    a: string;
}

type Valid = {
    defaultData: () => Foo;
};
type Validation<T> =
    T extends { defaultData: infer Fn }
    ? Fn extends (...args: any[]) => any
    ? ReturnType<Fn> extends Foo
    ? Foo extends ReturnType<Fn>
    ? T
    : never
    : never
    : never
    : never

const builder = <T extends Valid>(arg: Validation<T>) => arg

builder({
    defaultData: () => ({  // expected error
        a: "hello",
        b: "foo"
    })
})

builder({
    defaultData: () => ({  // ok
        a: "hello",
    })
})

For further insights on generic validation, you can visit my blog

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 RETRIEVING a particular cookie value in Angular version 14

"I've integrated the ngx-cookie-service library in my Angular project, but I'm experiencing an issue where two different cookies are being retrieved upon login even though the session id is already set in the backend. How can I ensure that m ...

What are the reasons for the inability to send form-data in Postman?

Encountering an issue when trying to send form-data in postman as Sequelize returns an error: value cannot be null However, everything works fine when sending a raw request with JSON. Have tried using body-parser and multer, but no luck. This is my inde ...

Provide a boolean value of true or false to indicate whether all delete operations were successfully completed

Currently, I am using Sequelize, GraphQL, and Typescript for my coding. Within my database, I have two tables named RecordInformation and OtherDescription. The RecordInformation table contains a foreign key called "OtherID" which references the OtherDescri ...

Running a TypeScript program that has been compiled with module resolution is not working as expected

I am currently in the process of compiling a TypeScript file with the following code: import { magic } from 'lib/magic'; magic(); The file structure looks like this: ./src/ main.ts lib/ a/magic.ts b/magic.ts Within ...

Experiencing a Typescript issue while trying to set a string as the state of a React component with a specified TS type

I've defined a state in my React component for a specific data type called Color. \\ state const [messageSeverity, setMessageSeverity] = useState<Color>('success'); \\ TS type export type Color = 'success&ap ...

Obtain references to templates in component classes

<div> <input #ipt type="text"/> </div> Can the template access variable be retrieved from the component class? For example, is it possible to retrieve it as shown below: class XComponent{ somefunction(){ //Is it possible t ...

The usage of @Inject('Window') in Angular/Jasmine leads to test failures, but removing it results in code failures

Currently, I am encountering a dilemma related to using Angular's @Inject('Window') within a web API service. This particular issue arises when the Window injection is utilized in the service constructor, leading to test spec failures in the ...

ways to coordinate two subscriptions so that one initiates only when the other one emits

Currently, I am developing an Angular application with a specific scenario. I have an observable signal named dataFetchedEvent$, which indicates that data has been fetched from a remote location. Additionally, there is a form that relies on this remote dat ...

Avoiding the use of reserved keywords as variable names in a model

I have been searching everywhere and can't find a solution to my unique issue. I am hoping someone can help me out as it would save me a lot of time and effort. The problem is that I need to use the variable name "new" in my Typescript class construct ...

Struggled with setting up the WebSocket structure in typescript

Issue Running the code below results in an error: index.tsx import WebSocket from 'ws'; export default function Home() { const socket = new WebSocket('ws://localhost:1919/ws'); return ( <div>Home</div> ); } ...

Tips for effectively handling the data received from a webservice when logging into a system

My web service provides me with permissions from my user. The permissions are stored as an array in JSON format. I need to find a way to access and display this data in another function. {"StatusCode":0,"StatusMessage":"Authenticated Successfully", "Token ...

How can you debug a Node.js CLI tool using WebStorm?

Struggling to develop a CLI tool using TypeScript within WebStorm as my IDE. No matter what I try, debugging just won't work for me. My journey in Node.js CLI programming started with this tutorial. Successfully transpiling the TS source with npx tsc, ...

Is it possible to include images in code comments within the Visual Studio Code environment?

Can images be inserted into code comments in VS Code? Currently, I am involved in an angular project where adding descriptive comments is crucial. While explaining a login process using just text may not be as effective, incorporating an image could enhanc ...

Dealing with client-side exceptions in a Next.js 13 application's directory error handling

After carefully following the provided instructions on error handling in the Routing: Error Handling documentation, I have successfully implemented both error.tsx and global-error.tsx components in nested routes as well as the root app directory. However, ...

Is it time to refresh the package-lock.json file?

I'm currently in the process of updating a React app from Node 14 to 16. One step I took during the upgrade was deleting the node_modules folder and package lock, then generating a new package-lock.json file. However, this resulted in numerous compila ...

Lazy loading in Angular 2 hits a snag when using a module that is shared

After successfully lazy loading my AccountModule, I encountered an issue when adding my SharedModule to it. All of my eagerly loaded modules seemed to be forgotten and I started receiving the following error: The component FoodComponent is not part of a ...

TS2345: Cannot assign type '(item: cType) => cType' to type '(value: Object, index: number, array: Object[]) => cType' within the parameter

I am currently working on a project using Angular 13 and Typescript 4.5.2. In addition, I am incorporating the Syncfusion library in my development process, specifically utilizing the datagrid component for managing table data. For reference, you can che ...

Assign a true or false value to every attribute of an object

Imagine we have a scenario with an interface like this: interface User { Id: number; FirstName: string; Lastname: string; Age: number; Type: string; } and a specific method for copying properties based on a flag. ...

Ways to populate missing cells with a default hyphen symbol

Looking for a way to default empty cells in my primeng datatable to '-'. Consider the following data: [ { 'column': null }, { 'column': { 'name': 'A' } }, { 'column': { 'name': ...

The 'target' property is not found on the specified 'string' type

I've encountered an issue while creating a search bar in Typescript. Despite my efforts, the input is not being recognized. It seems that whenever I use the term 'target' in my onChange method, it triggers an error stating: Property &ap ...