Ensure that the types of objects created by spreading are checked

When we spread two different types of objects to compose a new object in TypeScript, the type-checking is not automatically enforced. So how can we make sure that TypeScript performs the necessary checking?

type TypeA = {
    id: number;
}

type TypeB = {
    name: string;
}

const a: TypeA = { id: 3 };
const b: TypeB = { name: 'my' };

const c: TypeA = {
    ...a,
    ...b,
};

The variable c is of type TypeA, but there should be an error generated for including the property name which is not part of the type definition. However, no error occurs:

https://i.sstatic.net/DSvQJ.png

On the other hand, if we explicitly add a non-existing field, TypeScript correctly raises an error:

https://i.sstatic.net/5Fi84.png

Answer №1

In TypeScript, object types are either "open" or "extendible" rather than "closed" or "exact". When you define a type like interface Foo {a: string}, it means that a value must have a property named a with a string value, but it does not restrict the presence of other properties. This allows you to create another interface, such as

interface Bar extends Foo {b: number}
, indicating that every instance of Bar is also considered an instance of Foo. Therefore, interface and class hierarchies represent type hierarchies in TypeScript. In your specific case, since c has a valid TypeA, there is no error.

Although some users desire exact types, as mentioned in the long-standing issue microsoft/TypeScript#12936, they are currently unsupported in TypeScript. Exact types would enforce strict adherance to a specific set of properties without allowing any additional ones. For now, developers can rely on excess property warnings for object literals to catch unexpected properties at compile time.


If you encounter a situation where you require precise types in TypeScript, there are workarounds suggested in the aforementioned issue. One solution involves using a generic helper function to compare a value against an "exactified" version of the desired type by mapping excess properties to the impossible never type. Here's an example:

const exactly = <T extends object,>() =>
  <U extends T & { [P in Exclude<keyof U, keyof T>]: never }>(
    u: U): T => u;

const exactlyTypeA = exactly<TypeA>();

The exactlyTypeA() function will only accept values that precisely match type A, preventing any added properties from slipping through unnoticed. It's important to use this function early to avoid widening the type later on accidentally.

While these workarounds may not be foolproof and involve additional code, they offer a close alternative to true exact types in TypeScript.

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

Mapping type keys to camelCase in Typescript: a guide

As someone who is relatively new to typescript, I am eager to learn how to create a mapped type that converts keys from one type to another. Specifically, if I have a type where all the keys are written in snake case, how can I create a new type with camel ...

Understanding the significance of an exclamation point preceding a period

Recently, I came across this code snippet: fixture.componentInstance.dataSource!.data = []; I am intrigued by the syntax dataSource!.data and would like to understand its significance. While familiar with using a question mark (?) before a dot (.) as in ...

Creating a Redis client in Typescript using the `redis.createClient()` function

I'm currently trying to integrate Redis (v4.0.1) into my Express server using TypeScript, but I've encountered a small issue. As I am still in the process of learning TypeScript, I keep getting red underline errors on the host parameter within th ...

What could be the reason for my npm package installed globally to not be able to utilize ts-node?

Recently, I've been working on developing a CLI tool for my personal use. This tool essentially parses the standard output generated by hcitool, which provides information about nearby bluetooth devices. If you're interested in checking out the ...

Change the background color of a span element dynamically

I am currently working on implementing dynamic background coloring for a span tag in my Angular view that displays document types. The code snippet provided is as follows: <mat-card *ngFor="let record of records"> <span class="doc ...

Enhance user interaction in Angular 13 by animating a selected element using just one animation block

I am currently working on a one-page website project to enhance my Angular skills, and I'm facing a challenge with animating multiple DOM elements using a single animation. Defining the animation for each element individually seems like a cumbersome a ...

Implementing GetServerSideProps with Next-Auth: Error message - Trying to destructure property 'nextauth' from 'req.query' which is undefined

I encountered an issue while using the getServerSideProps function in Next.js with Next-Auth. The error I received was a TypeError: TypeError: Cannot destructure property 'nextauth' of 'req.query' as it is undefined. Upon checking with ...

Factory function with type constraints and default parameter causing TS2322 error

I have a base class that requires some parameters to be passed... class BaseClass<ItemType> { // Some irrelevant parameters omitted for simplicity... constructor(__items: Iterable<ItemType>) {} } Now, I want to create a factory func ...

The combination of Object.keys() and the find function

Having trouble figuring out why I'm getting an error when attempting to use ES6 .find on the following data in order to retrieve the record with id number 3. { {id:10,title:'Dairy & Eggs'} {id:7,title:'Laundry & Household'} {id ...

Utilizing lazy loading in conjunction with ngFor to optimize performance

I encountered the error Can't bind to 'ngForOf' since it isn't a known property of 'li'. Despite trying the suggested solutions, such as importing BrowserModule in the main module (app.module.ts) and importing CommonModule in ...

One method to make this code more concise

Is there a way to condense this code? I want 'All' to be displayed at index 0. Can I have multiple conditions, such as displaying 'All' at index 0, performing an action at every other index, and another action at the last index? I enc ...

The contents table remains fixed in the top right corner as you scroll

I have developed an Angular app with a table-of-contents component that only displays two items. The code for the script is as follows: ts import { Component, OnInit } from '@angular/core'; import { pdfDefaultOptions } from 'ngx-extended-p ...

Tips for accessing touch events within the parent component's area in React Native

I implemented the code below in my React Native app to disable touch functionality on a specific child component. However, I encountered an issue where the touch event was not being detected within the area of the child component. How can I fix this prob ...

Leverage TypeScript exclusively for type-checking JavaScript code with JSDoc annotations

I am looking to incorporate types in an existing JS project specifically for IDE syntax highlighting, without adding to the library @types/. For example, I have a file named 'TestComponent.js': export const TestComponent = (props) => { re ...

In which situations is it required to specify the return type of a function in TypeScript?

When it comes to making functions in typescript, the language can often infer the return type automatically. Take for instance this basic function: function calculateProduct(x: number, y: number) { return x * y; } However, there are scenarios where dec ...

Encountering a type error while trying to read dummy data, as the map function is not

I am currently working on fetching dummy data from a URL in my component using TS and Next.js. Unfortunately, I encountered an error type that I am unable to diagnose. typings.d.ts: export type Themen = { id: number; title: string; description: string; ...

Creating tests in JestJs for React components that utilize Context Provider and Hooks

As a newcomer to front-end development, I am currently working on authentication with Okta-React. To pass logged-in user information across multiple components, I'm utilizing React context with hooks. While this approach works well, I encountered an i ...

Is there a disparity in capabilities or drawbacks between ViewChild and Input/Output in Angular?

As I delve into Angular ViewChild and compare it to Input/Output parameters, I can't help but wonder if ViewChild has any drawbacks or limitations compared to Input/Output. It appears that ViewChild is the preferred method, as all parameters are now ...

Increasing an ID number automatically using Javascript

I'm currently working on a functionality where a unique number is automatically generated whenever a new record is created. For instance, if I were to click "Create record" on a webpage, the number would auto-fill in the record ID field. Subsequently, ...

Exploring the complexities of cyclic dependencies and deserialization in Angular

I have encountered an issue with deserializing JSON objects in my Angular project. After receiving data through httpClient, I realized that I need to deserialize it properly in order to work with it effectively. I came across a valuable resource on Stack O ...