What is the rationale behind permitting surplus properties in Typescript interfaces even though all properties are declared as optional?

Exploring the code snippet...

interface Options {
  allowed?: string;
}

function test(options: Options) {
  return options;
}

const options = {
  allowed: 'allowed',
  notAllowed: 'notAllowed',
};

test(options); // no error thrown

Why does adding notAllowed to options not result in an error in Typescript when calling test(options)?

Is there a way to modify this to disallow excess properties?

UPDATE 1...

If I change it to const options: Options = {...};, Typescript will raise an error.

Shouldn't Typescript automatically infer this since I specified that the options parameter should be of type Options?

If not, shouldn't there be an error when passing in const object = {...};?

If not, this could lead to issues for other developers using their own objects with the test function.

UPDATE 2...

The reason for this is to prevent errors in option bags caused by misspelled properties. For example, allowing only include and excluding includes. Is the only solution when passing an object variable (not a literal object) as a parameter to declare a property like includes?: null; in the Options interface?

Answer №1

One of the challenges with TypeScript is its lack of support for exact types, where properties are constrained only to those explicitly defined. If you're looking for excess property checking, that occurs in a specific scenario when using a fresh object literal.

An approach to handle this issue is to make your `test()` function generic in a manner that fails compilation upon detecting extra properties. The answer provided in the linked duplicate question elaborates on how this mechanism operates. Here's an example:

type Exactly<T, U> = T & Record<Exclude<keyof U, keyof T>, never>;
function test<T extends Exactly<Options, T>>(options: T) {
    return options;
}

const okay = { allowed: "okay" };
test(okay); // no error

const excess = {
    allowed: 'allowed',
    notAllowed: 'notAllowed',
};
test(excess); // error

Link to code

Answer №2

Is there a way to prevent excess properties in TypeScript?

Unfortunately, it is not possible to restrict excess properties in TypeScript due to its implementation of duck typing, where an object's type is based on the properties it has rather than a specific interface.

In simpler terms, if an object meets the requirements of a certain type (even if it contains additional properties), TypeScript will still consider it as that type.

Answer №3

When creating a test function for future developer use, it's advisable to avoid using interfaces in parameter typing. This is because the function may not work unless the object is properly typed before being passed to the test() function. A good alternative approach is to utilize optional parameters:

function test(allowed?: string) {
  return { allowed };
}

const options = {
  allowed: 'allowed',
  notAllowed: 'notAllowed',
};

test(); //works as expected
test(options.allowed); //works as expected
test(options.allowed, options.disallowed);  //throws an error

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

Encountering Problems when Converting Vue Application from JavaScript to TypeScript with Vue and Typescript

Currently, I am in the process of converting a Vue project from JavaScript to TypeScript without utilizing the class-style syntax. These are the steps I took: I ran: vue add typescript I went through all my .vue files and: Indicated that TypeScript ...

retrieve the checkbox formgroup using the Response API

After following a tutorial on creating dynamic checkboxes, I now need to implement dynamic checkboxes using an API request. In my implementation so far, I have defined the structure as shown below: inquiry-response.ts interface Item { // Item interface ...

Typescript: Unfiltering a string array

Seeking assistance with TypeScript syntax as a beginner. I'm struggling to refactor this code in order to retrieve the full list of serviceBranches. Currently, there is filtering and mapping resulting in only one serviceBranch being returned from our ...

Initiate and terminate server using supertest

I've developed a server class that looks like this: import express, { Request, Response } from 'express'; export default class Server { server: any; exp: any; constructor() { this.exp = express(); this.exp.get('/' ...

The value of the filename property cannot be determined as it is undefined

Hey everyone, I'm currently working on a project using nestjs and reactjs. I encountered an error when trying to add a document that reads: "Cannot read properties of undefined (reading 'filename') in multer.config.ts" import { diskStorag ...

Enhance a subject's behavior by overriding the .next method using a decorator

Currently, I am working on an Angular application where I have numerous Subjects, BehaviorSubjects, and ReplaySubjects as properties in various services. I am attempting to create a TypeScript decorator that can be added to some of these Subjects to enhanc ...

Angular 7 introduces updates to the way lists are ordered

I am facing an issue with my code that calls an API for each object in a list called "titles" and then adds the object to another list named "groupDocs". However, due to the asynchronous nature of the API response, the order of objects in the "groupDocs" l ...

JavaScript: Translating Date into Moment

Is there a way to convert a Date object to Moment in JavaScript? let testDate = new Date(2020, 05, 03, 1, 2); I attempted the following code without success toMoment(testDate) What is the correct syntax to achieve this conversion? ...

Associate a unique identifier string with a randomly generated integer identifier by Agora

For my current web project, I am utilizing a String username as the UID to connect to the channel in an Agora video call. However, I now need to incorporate individual cloud recording by Agora into the project. The challenge lies in the fact that cloud r ...

What is the method for referencing a subtype within an established type?

When working with React-native, I came across a component called FlatList which includes a property known as ListHeaderComponent. My question is how to specify the type of this property without having to manually copy and paste the original type. Currentl ...

Make TypeScript parameter optional if it is not supplied

I am working with an interface that defines scenes and their parameters: export interface IScene<R extends string> { path: R; params?: SceneParams; } The SceneParams interface looks like this: export interface SceneParams { [key: string]: s ...

Issue with dependencies: Incorrect value passed to `ts.resolveTypeReferenceDirective` causing a problem

This issue is really frustrating me. I'm currently working on this repository. Everything seems to be fine on the client side, but when it comes to the server side, I encountered the following problem: MacBook-Pro$ yarn dev yarn run v1.22.19 warning . ...

Starting up various modules in Angular 6 using arrays

Can an array be declared and used to bootstrap multiple components in module.ts? I attempted the following: export var initialComponents = []; initialComponents.push(AppComponent); if(condition) { initialComponents.push(IchFooterComponen ...

Vue3 project encountering issues with Typescript integration

When I created a new application using Vue CLI (Vue3, Babel, Typescript), I encountered an issue where the 'config' object on the main app object returned from the createApp function was not accessible. In VS Code, I could see the Typescript &ap ...

When an empty array is returned from a catch statement in an async/await method, TypeScript infers it as never

Function getData() returns a Promise<Output[]>. When used without a catch statement, the inferred data type is Output[]. However, adding a catch statement in front of the getData() method changes the inferred data type to Output[] | void. This sugge ...

When converting a PDF to a PNG, the precious data often disappears in the process

I am currently facing a problem with the conversion of PDF to PNG images for my application. I am utilizing the pdfjs-dist library and NodeCanvasFactory functionality, but encountering data loss post-conversion. class NodeCanvasFactory { create(w, h) { ...

Retrieve the value of a hidden input when a button is clicked using reactive forms in Angular

I am currently attempting to retrieve the values of hidden input fields that are dynamically added when the user clicks on the "insert more" button. If you'd like to view the code in action, you can visit this StackBlitz link: get hidden input value ...

Tips for handling catch errors in fetch POST requests in React Native

I am facing an issue with handling errors when making a POST request in React Native. I understand that there is a catch block for network connection errors, but how can I handle errors received from the response when the username or password is incorrec ...

Troubleshooting the "Request failed with status code 500" error when refreshing a page in a React application

Every time the page is reloaded, an error message pops up saying: Uncaught (in promise) Error: Request failed with status code 500. Here's the code in list.tsx: const [state, setState] = useState([]); const { getRoom } = useRoom(); const fe ...

Incorporating an interface into a data property within a router using TypeScript and Angular

Within the app-routing-module.ts file, utilizing the data property allows us to include custom fields/variables as shown below: ... { path: 'admin-board', loadChildren: './admin-board/admin-board.module#AdminBoardPageModule', dat ...