The shape-matching subset functionality in Typescript is experiencing issues

One of the key principles of TypeScript is that type checking focuses on the structure of values, a concept known as duck typing or structural typing. This means that only a subset of an object's fields needs to match for it to be considered compatible.

For instance, in TypeScript, the following code is valid:

interface Point {
  x: number;
  y: number;
}

function logPoint(p: Point) {
  console.log(`${p.x}, ${p.y}`);
}

const rect = { x: 33, y: 3, width: 30, height: 80 };
logPoint(rect); // logs "33, 3"

However, if you directly call the logPoint function with an object literal, TypeScript will raise a warning:

logPoint({ x: 33, y: 3, width: 30, height: 80 }); // error

So, why does subset type-matching work in the first case but not in the second?

Links:

Reference on structural type system

First example

Second example

Answer №1

Typescript's strictness levels vary depending on the context in which you are working.

When you define an object with explicit types, Typescript enforces strict rules and does not allow any additional properties to be added. This explains why your second example triggers an error: in the given context, the object can only be of type Point, but you have included extra properties which violate this rule.

Another instance of this strict checking is demonstrated below. Even though I have specified that a Point object is being created, it includes unnecessary details:

const rect: Point = { x: 33, y: 3, width: 30, height: 80 };

In contrast, your first example does not face the same level of strictness. Since the object creation line lacks a specific type declaration, Typescript assigns a generic type

{ x: number, y: number, width: number, height: number }
. Consequently, when passing this object as an argument, Typescript focuses on whether the types match rather than strictly adhering to the predefined structure.

Answer №2

@jonrsharpe is absolutely correct

This concept is referred to as: excess property check

When assigning object literals to variables or passing them as arguments, they undergo special treatment known as excess property checking. If an object literal contains properties not present in the "target type," an error will occur:

If you still prefer using references, there is a workaround available:

interface Point {
  x: number;
  y: number;
}

type IsPoint<T> = T extends Point ? Point extends T ? T : never : never;

function logPoint<T>(p: IsPoint<T>) {
  console.log(`${p.x}, ${p.y}`);
}

const rect = { x: 33, y: 3, width: 30, height: 80 };
const rect2 = { x: 33, y: 3};

logPoint(rect); // This will result in an error, as Point does not include width and height properties

logPoint(rect2) // This is fine 

logPoint({ x: 33, y: 3, width: 30, height: 80 }); // Will also generate an error

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

The creation of the Angular project was unsuccessful due to the outdated circular-json dependency

After attempting to create a new Angular project with the command: ng new hello-world I encountered an error message: npm WARN deprecated <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="b5d6dcc7d6c0d9d4c798dfc6dadbf5859b809b8 ...

NestJS: Specify the data type for @Body()

Consider the code snippet below: @Post() public async createPet(@Body() petDetails: PostPetDto): Promise<any> { } In this scenario, the type of @Bod() petDetails defaults to plain/any instead of the declared type of PostPetDto. What is the recommen ...

Building a React Typescript service with axios functionality

When creating a service and calling it from the required functional component, there are two different approaches you can take. 1. export const userProfileService = { ResetPassword: async (userId: string) => { var response = await http.get ...

Backend communication functions seamlessly within the service scope, yet encounters obstacles beyond the service boundaries

I'm facing an issue with accessing data from my backend. Although the service successfully retrieves and logs the data, when I try to use that service in a different module, it either shows "undefined" or "Observable". Does anyone have any suggestions ...

Ways to simulate a dependent class in TypeScript & JEST without modifying constructor parameters to optional

Currently, I am attempting to replicate a well-known process in Java development using TypeScript and JEST for practice. In this scenario, there is a Controller class that relies on a Service class. The connection between the two is established through the ...

Is subtyping causing issues in TypeScript's inheritance model?

I am currently utilizing TypeScript for my coding projects, and I have observed that it can allow the production of non-type-safe code. Despite implementing all the "strict" options available to me, the behavior I am experiencing goes against the principle ...

Give it a little time before uploading a widget onto the page

As a newcomer to programming, I recently came across this code from an open source project. I am in the process of loading a widget onto a website. Instead of having the widget load instantly, I would like it to wait 10 seconds before displaying. Below i ...

Having trouble extracting a list of matches using a Regular Expression?

const stringWithDate: string = "4/7/20 This is a date!"; const reg: RegExp = new RegExp("^(\d{1,2}\/\d{1,2}\/\d{1,2})").compile(); const exist: boolean = reg.test(stringWithDate) const matches: RegExpExecArray | null = reg.exec(str ...

Select multiple rows by checking the checkboxes and select a single row by clicking on it in the MUI DataGrid

I am currently utilizing the MUI DataGrid version 4 component. The desired functionalities are as follows: Allow multiple selections from the checkbox in the Data Grid (if the user selects multiple rows using the checkbox). Prevent multiple selections fr ...

DeActivation only occurs once per route

Having an issue with the CanDeActivate() function in Angular2. The problem arises when a user tries to leave a page while in edit mode, triggering a popup with Yes, No, and Cancel options. If the user clicks on Cancel, the popup closes. If they click on No ...

Why am I encountering this rendering issue when passing data to the ReactTable component?

The following code snippet represents the parent component containing an array of columns and data. const TransactionTable = () => { const columns = useMemo( () => [ { Header: 'DATE/TIME', accessor: &apos ...

Deploying an Azure Blob Trigger in TypeScript does not initiate the trigger

After successfully testing my Azure function locally, I deployed it only to find that it fails to trigger when a file is uploaded to the video-temp container. { "bindings": [ { "name": "myBlob", "type&qu ...

Angular 2: Enhancing User Experience with Pop-up Dialogs

Looking to implement a popup dialog that requests user input and returns the value. The popup component is included in the root component, positioned above the app's router outlet. Within the popup component, there is an open() method that toggles a ...

Does the React memo function modify the component's prop type?

I've come across a strange issue where defining two components causes compilation errors when written separately but not when written in the same file. test3.tsx import React from "react"; type ValueType = number[] | string[] | number | st ...

What are some strategies for enhancing TypeScript/Node speed in VSCode with the help of WSL2 and Docker?

My development environment consists of VSCode running on Windows (v1.58.2) with the Remote WSL extension (v0.58.2). I have Docker Desktop (3.5.2, engine: 20.10.7) set up to work with Linux containers through the WSL2 backend. Within these containers, I a ...

Steps for referencing a custom JavaScript file instead of the default one:

Currently, I am utilizing webpack and typescript in my single page application in combination with the oidc-client npm package. The structure of the oidc-client package that I am working with is as follows: oidc-client.d.ts oidc-client.js oidc-client.rs ...

The type string[] cannot be assigned to type 'IntrinsicAttributes & string[]'

I'm attempting to pass the prop of todos just like in this codesandbox, but encountering an error: Type '{ todos: string[]; }' is not assignable to type 'IntrinsicAttributes & string[]'. Property 'todos' does not ex ...

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 ...

Using JSDoc with "T extending Component"

get_matching_components<T extends Component>(component_type_to_return: { new (doodad: Doodad): T }): T[] { return this.components.filter(component => component instanceof component_type_to_return) } In TypeScript, I created a method to retrie ...

Centralize all form statuses in a single component, conveniently organized into separate tabs

I am working on a component that consists of 2 tabs, each containing a form: tab1.component.ts : ngOnInit() { this.params = getParameters(); } getParameters() { return { text : 'sample' form: { status: this.f ...