Using TypeScript: creating functions without defining an interface

Can function props be used without an interface?

I have a function with the following properties:

from - HTML Element

to - HTML Element

coords - Array [2, 2]

export const adjustElements = ({ from, to, coords }) => {
    let to_rect = to.getBoundingClientRect(),
        width = to_rect.width * ((coords[0] - 1) * 0.5),
        height = to_rect.height * ((coords[1] - 1) * 0.5);

    from.style.top = (to_rect.top + height) + "px";
    from.style.left = (to_rect.left + width) + "px";
}

Upon running the code, I encounter the following errors:

Binding element 'from' implicitly has an 'any' type.

Binding element 'to' implicitly has an 'any' type.

Binding element 'coords' implicitly has an 'any' type.

This issue arises due to the lack of an interface in the function. Creating an interface for each function seems cumbersome since there are multiple functions like this one, each with different parameters. Is there a way in typescript to avoid the requirement of an interface for every function?

Answer №1

When the compiler cannot deduce the types of the from, to, and coords variables extracted from the function parameter, it defaults to inferring the the any type, which is inherently risky and allows for unrestricted operations without further error detection. In scenarios where you have enabled the --strict compiler features or at least the --noImplicitAny flag, this defaulting to any will trigger a warning to alert you about the loss of type safety.


The recommended approach is to specify the type for the function parameter to explicitly inform the compiler about its expected form. If you still wish to utilize any and bypass the warning, you can do so by clearly indicating it:

export const adjustElements = ({ from, to, coords }: any) => {
  let to_rect = to.getBoundingCleintRect(),
    width = to_rect.width * ((coords[0] - 1) * 0.5),
    height = to_rect.heigth * ((coords[1] - 1) * 0.5);

  from.style.top = (to_rect.top + height) + "px";
  from.style.left = (to_rect.left + width) + "px";
} // no error

However, taking this route allows for various problematic calls to adjustElements which may lead to unexpected behavior:

adjustElements("oopsie"); // no error

This permissive setup also enables erroneous implementations; for instance, in the aforementioned code, misspellings such as getBoundingClientRect and to_rect.height go unnoticed by the compiler due to the broadness of any. Hence, avoiding such an approach is advised.

Instead, establishing the correct type for the function parameter is crucial. As determined earlier, the from and to attributes should be of type HTMLElement, while the coords attribute ought to represent a tuple consisting of two numeric elements.

You could define a named interface for this object structure. However, if this structure is only utilized once, creating an explicit name might not be necessary. In such cases, employing an anonymous object type suffices:

export const adjustElements = ({ from, to, coords }:
  { from: HTMLElement, to: HTMLElement, coords: [number, number] }
) => {
  let to_rect = to.getBoundingClientRect(),
    width = to_rect.width * ((coords[0] - 1) * 0.5),
    height = to_rect.height * ((coords[1] - 1) * 0.5);

  from.style.top = (to_rect.top + height) + "px";
  from.style.left = (to_rect.left + width) + "px";
}

With this adjustment, the code operates effectively, errors are promptly flagged by the compiler, and illegitimate invocations of adjustElements() are identified:

adjustElements("oopsie"); // error
// Argument of type 'string' is not assignable to parameter of 
// type '{ from: HTMLElement; to: HTMLElement; coords: [number, number]; }

An occurrence considered invalid by the compiler includes:

adjustElements({
  from: document.querySelector(".wheel-pointer"),
//~~~~ <-- Type 'HTMLElement | null' is not assignable to type 'HTMLElement'
  to: document.querySelector(".wheel"),
//~~ <-- Type 'HTMLElement | null' is not assignable to type 'HTMLElement'
  coords: [2, 1]
}) // error!

The resulting errors pinpoint the issue accurately: the likelihood that the from and to properties may hold a value of null due to the ambiguous return of document.querySelector(). To rectify this, ensure that these properties are non-null either through verification or assertion. Verification utilizing truthiness narrowing involves:

const wheelPointerElement = document.querySelector(".wheel-pointer");
if (!wheelPointerElement) throw new Error("OH NO");
const wheelElement = document.querySelector(".wheel");
if (!wheelElement) throw new Error("WHY ME");

adjustElements({
  from: wheelPointerElement,
  to: wheelElement,
  coords: [2, 1]
}) // okay

Whereas assertion via the non-null assertion operator ! entails:

adjustElements({
  from: document.querySelector(".wheel-pointer")!,
  to: document.querySelector(".wheel")!,
  coords: [2, 1]
}) // okay

To delve into the nuances between checking and asserting, along with their respective advantages and drawbacks, requires extensive elaboration beyond the scope of this discussion. The core principle remains—such error scenarios exemplify TypeScript's preemptive error notification feature. Unlike traditional JavaScript deployments, TypeScript offers forewarning on potential runtime issues like calling

adjustElements(null, null, [2, 1])
, prompting the developer to address them beforehand.

Access Playground link for the code

Answer №2

UPDATE: After receiving feedback from jcalz in the comments, it was brought to my attention that the TypeScript code I provided is incorrect. I overlooked the fact that you should be passing three separate arguments instead of a single argument wrapped in {}.


To ensure TypeScript can correctly interpret the types of your function's arguments, you must explicitly declare them. If you omit this information and TypeScript cannot infer the types (e.g. with default values), it will default to any. However, implicit any types are not allowed by the default compiler settings.

Using an interface is not mandatory; you can achieve this without one:

export const adjustElements = ({ from: HTMLElement, to: HTMLElement, coords: [number, number] }) => {

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

Problem with sortable columns in DataTables

$(document).ready(function () { var dt = $('#example').DataTable({ "processing": true, "serverSide": true, "ajax": "api.php?t=clients", "columns": [{ "className": "details-control", ...

Encountering a sanity issue while attempting to execute vercel build: sanity/..page.jsx lacks a root layout. To resolve this issue, ensure that each page includes a root layout

While my project runs smoothly locally, I encounter issues when trying to deploy it on Vercel. The error message reads: ⨯ sanity/[[...index]]/page.jsx doesn't have a root layout. To resolve this issue, ensure that every page has a root layout. Comp ...

Creating a phonecat application using Angular on a node server

As a newcomer to Angular, I am currently in the process of setting up the Angular phonecat application. To begin with, I downloaded the code from this location: https://docs.angularjs.org/tutorial/ After that, I installed Node.js on my system. Now, I a ...

Show a specific item when selecting an option from a dropdown menu

Hey there, I have a question regarding creating a theme for the Genesis Framework. Right now, I'm facing a challenge with one particular element. Here's the situation: I've got two drop-down lists, but I only want the second one to be visib ...

Utilizing Angular Pipes for Utilizing Location

The Location service in Angular is described as "A service that applications can use to interact with a browser's URL." One of its methods, getState(), provides "the current state of the location history," while another method, subscribe(), allows us ...

AngularJS - Sending event to a specific controller

I am facing an issue with my page where a list of Leads each have specific actions that are represented by forms. These forms can be displayed multiple times on the same page. Each form has its own scope and controller instance. After submitting a form, an ...

Launching an Angular application from the local server

I have successfully deployed an Angular frontend on a server. It is functioning well, with three scripts: /runtime.0fad6a04e0afb2fa.js /polyfills.24f3ec2108a8e0ab.js /main.a9b28b9970fe807a.js My goal is to start this application in Firefox without ...

Managing Flicker Effect by Implementing Theme Switching and Using Local Storage in Next.js with Ant Design

I've been working on a new feature to switch themes (light/dark) dynamically in a Next.js application using Ant Design. Successfully integrating the theme switch with a toggle switch and useState hook, I'm faced with the challenge of storing the ...

Angular 2: Utilizing Http Subscribe Method with "this" Context Pointer

Question: http.request('js/app/config/config.json').subscribe(data => { this.url = data.json().url; }); It seems that "this" is pointing to Subscriber instead of the parent class. I was under the impression that the fat- ...

index signature in TypeScript is an optional feature

Is it possible to create a type with optional namespaces in TypeScript? export interface NodesState { attr1: number; attr2: number; attr3: number; } The goal is to allow users to namespace the type like this: { namespace1: { attr1: 100, ...

"Uncaught ReferenceError: $ is not defined - $function()" error in JavaScript/jQuery

When attempting to execute a JavaScript/jQuery function, an error is encountered when using Firebug: $ is not defined $(function()". The issue arises from the placement of the JavaScript code within a file named core.js that is referenced by index.php. W ...

Incorrect configuration file for karma

To simplify the configuration of the karma config file, I have utilized variables to store specific parts of the path. var publicfolder = "public" var mypath = "packages/vendor/package/"; files: [ publicfolder+'/assets/libs/jquery/dist/jquery.js&a ...

Using TypeORM's QueryBuilder to select a random record with a nested relation

Imagine a scenario where I have the following entities: User @Entity('user', { synchronize: true }) export class UserEntity { @PrimaryGeneratedColumn('uuid') id: string; @Column() firstName: string; @Column() lastName: s ...

Guide on associating user IDs with user objects

I am currently working on adding a "pin this profile" functionality to my website. I have successfully gathered an array of user IDs for the profiles I want to pin, but I am facing difficulties with pushing these IDs to the top of the list of profiles. My ...

Analyze the information presented in an HTML table and determine the correct response in a Q&A quiz application

I need to compare each row with a specific row and highlight the border accordingly: <table *ngFor="let Question from Questions| paginate: { itemsPerPage: 1, currentPage: p }"> <tr><td>emp.question</td></tr> <tr> ...

AngularJs input field with a dynamic ng-model for real-time data binding

Currently facing an issue with my static template on the render page. <form name="AddArticle" ng-submit="addArticle()" class="form add-article"> <input type="text" value="first" init-from-form ng-model="article.text[0]" /> <input typ ...

How can you efficiently update another ng-model value based on a select input in AngularJS using ng-change?

<select data-ng-init="selectedItem='previewWidth = 1920; previewHeight = 1080'" data-ng-model="selectedItem" data-ng-change="GetNewData(); {{selectedItem}}"> <option value="previewWidth = 1920; previe ...

What is the syntax for implementing this function in TypeScript?

When developing applications in react and typescript, I find myself frequently creating helper functions. However, there are two key points that always give me pause. One of my functions is provided below, showcasing the common dilemmas I face. What shoul ...

Replace the content within the iFrame completely

Is it possible to have a textarea where I can input HTML code and see a live preview of the webpage in an iframe as I type? For example, here is the code I'd like to write in the textarea: <!DOCTYPE html> <html> <head> ...

What do you call the syntax %< ... >%?

Observed zoomInAnimation : true, zoomOutScale : false, templateLegend : "<ul class=\"<%=type.toLowerCase()%>-legend\"><% for (var j=0; j<sections.length; j++){%><li><span style=\"background-color:<%=section ...