What is the best way to explain a function that alters an object directly through reference?

I have a function that changes an object's properties directly:

function addProperty(object, newValue) {
  object.bar = newValue;
}

Is there a way in TypeScript to indicate that the type of object is modified after calling the addProperty function? I am looking for something like this:

interface UpdatedProperty<T> {
  bar: T;
}

function addPropertyMutatesTo<Original extends object, New>(object: Original transforms into (Original & UpdatedProperty<New>), newValue: New) {
  object.bar = newValue;
}

const dataObject = { foo: 'bar' };
dataObject.bar; // TS: error
addProperty(dataObject, 123);
dataObject.bar; // TS: ok

Answer №1

The type system in TypeScript is not primarily designed to mutate the types of variables, but it does utilize control flow analysis to narrow variable types within certain code blocks. However, until recently, there was no straightforward way to trigger such narrowing through functions like addProp().

With the introduction of assertion functions in TypeScript 3.7, developers now have a tool to create functions that guard against invalid states without returning a value. These assertion functions are relatively new and come with some limitations, as discussed in various GitHub issues.

One approach to achieve type narrowing with functions like addProp() is to define an assertion function where the input type T1 gets asserted to the narrower type T1 & WithProp<T2>. In this setup, if the input does not match the expected type, the function simply modifies the object to match that type by adding the necessary property.

function addProp<T1 extends object, T2>(
    o: T1,
    value: T2
): asserts o is T1 & WithProp<T2> {
    (o as T1 & WithProp<T2>).foo = value;
}

This allows for the desired behavior when accessing properties:

let obj = { bar: 'baz' };
obj.foo; // error! Property 'foo' does not exist on type {bar: string}
addProp(obj, 123);
obj.foo; // okay

It's important to note that this type narrowing occurs due to control flow analysis, which resets upon reassignments. This means that directly reassigning values might result in unexpected errors related to type mismatches.

Overall, while this method represents a step towards incorporating mutations in the type system, there are still limitations to consider. Good luck experimenting with these concepts!

Link to code example

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

Show a condensed version of a lengthy string in a div using React TS

I've been tackling a React component that takes in a lengthy string and a number as props. The goal of the component is to show a truncated version of the string based on the specified number, while also featuring "show more" and "show less" buttons. ...

Having trouble resolving modules with Angular 2 and ASP.NET 5 (unable to locate angular2/core)?

I am diving into a fresh ASP.NET5/MVC6 project and facing some challenges along the way. Struggle 1 When I opt for classic as the moduleResolution in my tsconfig.json, I encounter an error stating: Cannot locate module 'angular2/core' Strugg ...

Discover the steps to extend static generic methods in Typescript

My issue lies in compiling Typescript code as the compiler doesn't seem to recognize the inheritance between my classes. Whenever I attempt to compile, an error arises: Property 'create' does not exist on type 'new () => T'. ...

Problem with extending a legacy JavaScript library using TypeScript

Can someone assist me with importing files? I am currently utilizing @types/leaflet which defines a specific type. export namespace Icon { interface DefaultIconOptions extends BaseIconOptions { imagePath?: string; } class Default exte ...

What is the process of determining if two tuples are equal in Typescript?

When comparing two tuples with equal values, it may be surprising to find that the result is false. Here's an example: ➜ algo-ts git:(master) ✗ ts-node > const expected: [number, number] = [4, 4]; undefined > const actual: [number, number] ...

Higher-Order Component integrated with HTMLElement

Check out this complex code snippet I created: export type AdvancedHoverElementProps<TElement extends HTMLElement> = React.HTMLProps<TElement> & { hoverDuration: number, onHoverChanged: (isHovering: boolean) => void }; export ...

Transferring various sets of information into a single array

In an attempt to generate JSON data in an array for passing to another component, I followed a method outlined below. However, being relatively new to this field, my execution may not have been optimal as expected. employeeMoney: any[] = []; employeeId: an ...

React - The `component` prop you have supplied to ButtonBase is not valid. Please ensure that the children prop is properly displayed within this customized component

I am attempting to use custom SVG icons in place of the default icons from Material UI's Pagination component (V4). However, I keep encountering this console error: Material-UI: The component prop provided to ButtonBase is invalid. Please ensure tha ...

NestJS's "Exclude" decorator in class-transformer does not exclude the property as expected

I attempted to exclude a specific property within an entity in NestJS, but it appears that the exclusion is not working as expected. When I make a request, the property is still being included. Code: // src/tasks/task.entity.ts import { Exclude } from &ap ...

Nested asynchronous functions in TypeScript

profile.page.ts: username: string; totalScore: number; ... loadUserData() { this.spinnerDialog.show(); this.firebaseServie.loadUserData().then(() => { this.username = this.sessionData.getUser().getUsername(); this.totalSco ...

To initiate the development environment, execute the following command: `cross-env NODE_ENV=

[email protected] start /Users/ssurisettii/Documents/il-17g5-app cross-env NODE_ENV=development npm run webpack-development sh: cross-env: command not found npm ERR! code ELIFECYCLE npm ERR! syscall spawn npm ERR! file sh npm ERR! errno ENOENT npm ER ...

Error message: The database query function is encountering an issue where the property 'relation.referencedTable' is undefined and cannot be accessed

Currently, I am working with two schemas named products.ts and category.ts. The relationship between these files is defined as one-to-many. In the products.ts file: import { pgTable, timestamp, uuid, varchar } from "drizzle-orm/pg-core"; import ...

Ways to observe redux action flow within a component

I am currently developing a React application structured with the following elements: redux typesafe-actions redux-observable My query is: How can I trigger a UI action when a specific redux action occurs? For instance, if we have these asynchronous ac ...

Declaration of React conditional props in TypeScript

I am facing an issue with my React component where the type is determined by a runtime variable (isMock). I am struggling to get the TS declarations to function properly: The component can either be MockedProvider or ApolloProvider from @apollo/client, ea ...

What is the best way to retrieve an accurately matched array?

I am working on a function that analyzes a string of DNA and should return an accurately matched DNA array. Here is the code snippet I have experimented with: function checkDNA(dna) { var dnaarr = []; for(var i = 0; i < dna.length; i++) { ...

Is There a Comparable Feature to *ngIf in DevExtreme?

Currently, I am diving into the world of webapp development using DevExtreme. As a novice in coding, this is my first time exploring the functionalities of DevExtreme. Essentially, I am seeking guidance on how to display certain elements based on specific ...

The Angular JavaScript page successfully compiles, yet displays only a blank screen

I am facing an issue with my Angular app where it compiles successfully, but the HTML page appears blank and my application is not displaying properly. I have encountered similar problems in the past which were often related to Imports, but this time I&apo ...

Invoke a function within the <img> tag to specify the source path

I have been attempting to achieve something similar to the following: <img id="icon" class="cercle icon" src="getIcon({{item.status}})" alt=""> This is my function: getIcon(status){ switch (status) { case 'Ongoing': ret ...

The TypeScript compiler is unable to locate the module react-scripts within the lerna webpack configuration

Recently, I've been working on setting up a new project using lerna, react-scripts, webpack, and sass. Here is my current directory structure: myApp /packages /myReactApp -> a react create app application /tsconfig.json /package ...

The value 'var(--header-position)' cannot be assigned to type 'Position or undefined'

Description of Issue I am attempting to utilize a CSS Custom Property to customize a component within a nextjs application using TypeScript. Strangely, all CSS properties accept the CSS variables except for the position property which triggers the error b ...