What sets apart TypeScript's indexed types from function signatures?

I am curious about the distinction between indexed type and function signatures in TypeScript.

type A = {
  _(num: number): void;
}['_'];

type B = {
  _(ten: 10): void;
}['_'];
let a: A = (num) => {
  console.log(num);
};

let b: B = (ten) => {
  console.log(ten);
};

a = b;
b = a;

In the code snippet above, there are no errors. However, when looking at the following code:

type A = (num: number) => void;

type B = (ten: 10) => void;

let a: A = (num) => {
  console.log(num);
};

let b: B = (ten) => {
  console.log(ten);
};

a = b;  // ❌
b = a;

An error is thrown:

Type 'B' is not assignable to type 'A'.
  Types of parameters 'ten' and 'num' are incompatible.
    Type 'number' is not assignable to type '10'.ts(2322)

What sets them apart? Why does the first block of code not generate an error?

Answer №1

The issue with the compiler arises from a concept known as contravariance, where the sub-typing relationship reverses for function inputs.


Consider a scenario where a "plastic bottle" is a subtype of "garbage". What happens when you compare bins that specifically only accept plastic bottles to those that can accommodate any type of garbage?

In this context, a "general garbage bin" acts as a subtype of a "plastic bottle bin". If you place a plastic bottle bin where people are expecting a general garbage bin, individuals may end up disposing of other types of trash that the plastic bottle bin cannot handle.

10 is considered a subtype of number. This implies that (num: number) => void is also a subtype of (ten: 10) => void

Therefore, in your second example, while b = a compiles without errors, a = b does not since B is not a subtype of A. This particular behavior was introduced in 2017 and can be enabled using the --strictFunctionTypes flag. Disabling this flag would allow both examples to compile successfully.


Why then does the first example compile when acquiring function types via indexed access?

According to the documentation regarding this flag:

During the development phase of this feature, various potentially unsafe class hierarchies, including some within the DOM, were identified. As a result, this setting exclusively applies to functions defined in function syntax and not those written in method syntax.

If needed, we can convert the method into a property featuring a function type:

type A = {
  _: (num: number) => void;
}['_'];

type B = {
  _: (ten: 10) => void;
}['_'];

Following this transformation, attempting a = b will fail to compile similar to the outcome observed in your second 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

NextJS 13 causes tailwind to malfunction when route group is utilized

I've encountered an issue in my NextJS 13 application where Tailwind classes are no longer being applied after moving page.tsx/layout.tsx from the root directory to a (main) directory within the root. I suspect that there may be a configuration that i ...

How to calculate the sum of all values in a FormArray in Angular

I am trying to retrieve the input values from each row and then calculate the sum of these rows. Here is my HTML code: <ng-container formArrayName="cap_values"> <tbody *ngFor="let item of capValues.controls; let i=index" [formGroupName]="i"& ...

The JavaScript function for converting a date to a local string in the format of DD MMM YYYY is causing an error message in the browser console stating that it is not a valid function

I am encountering an issue with formatting a date string. The date is currently in the format 2021-03-31T00:00:00, and I need it to be displayed as 31 Mar 2021. In my TypeScript code, I attempted to use the following function: const formattedDate = i.Susp ...

Issue: The function "generateActiveToken" is not recognized as a function

I encountered an issue in my Node.js project and I'm unsure about the root cause of this error. Within the config folder, there is a file named generateToken.js which contains the following code snippet: const jwt = require('jsonwebtoken'); ...

Angular obtains an undefined response from a service

Having trouble retrieving the data from my service. Working with Angular 8. Here's the code snippet: Within the service: query(url: string) { this.subscription = this.socket$.subscribe( (message) => { return message; }, ...

display the picture depending on the circumstances

Is there a way for the image tag to display different images based on a condition? I attempted the code below, but it's not displaying the image: <img src="{{row.image}}!=null?'data:image/jpeg;base64,{{row.image}}':'./assets/img/qu ...

Utilizing ag-grid with Vue.js: Implementing TypeScript to access parent grid methods within a renderer

I've integrated ag-grid into my project and added a custom cell renderer: https://www.ag-grid.com/javascript-grid-cell-rendering-components/#example-rendering-using-vuejs-components Although the renderer is working well, I'm facing an issue whe ...

Adding a unique font to the themeprovider within styled components: A step-by-step guide

In the process of developing a React application using material-ui styled-components along with TypeScript. The task at hand is to incorporate a custom font into my styled components, but I'm facing challenges in making it functional. My initial ste ...

Issue encountered when trying to integrate Angular2 with Visual Studio 2015 Update

Starting my journey with Angular2 in Visual Studio 2015. Following advice from this helpful article. Encountering an issue when running the index.html file, it gets stuck on 'Loading...'. Here are the key configurations and code files being use ...

Jasmine attempting to access a nonexistent property

I created a very basic component: import { Component } from '@angular/core'; @Component({ selector: 'loading', templateUrl: './loading.component.html', styleUrls: ['./loading.component.scss'] }) export ...

The different types of property 'cacheLocation' do not match

I have been working on updating an old React app from JavaScript to Typescript gradually. I started by migrating the configuration file, but encountered an error when renaming it to .TS. Here is the error message: /Users/xx/yy/lulo/src/adalConfig.ts (13, ...

Issue with Nestjs validate function in conjunction with JWT authentication

I am currently implementing jwt in nest by following this guide Everything seems to be working fine, except for the validate function in jwt.strategy.ts This is the code from my jwt.strategy.ts file: import { Injectable, UnauthorizedException } from &ap ...

The specified module '...' is identified as a non-module entity and therefore cannot be imported using this specific construct

Currently, I am facing an issue in my .tsx file where I am attempting to import a RaisedButton component from material-ui using the following code: import * as RaisedButton from 'material-ui/lib/raised-button' Unfortunately, this is triggering ...

What is the reason for recursion not producing a new object as output?

Trying to filter out nodes in a recursion function that iterates through a tree based on the registry property. function reduceNodesRegistry(source: any) { if (!source.registry) return source; return { ...source, children: s ...

Utilizing external applications within Angular applications

I have the task of creating a user interface for clocker, a CLI-based issue time tracker. Clocker functions as a stand-alone node.js application without any programming interface. To begin tracking time for an issue labeled 123, the command would typically ...

What is the method for defining a monkey patched class in a TypeScript declarations file?

Let's say there is a class called X: class X { constructor(); performAction(): void; } Now, we have another class named Y where we want to include an object of class X as a property: class Y { xProperty: X; } How do we go about defining ...

What is the reason behind TypeScript rejecting the syntax of checkbox input elements?

When trying to use the following checkbox in TypeScript, I encountered a warning: <input type="checkbox" onClick={(event: React.MouseEvent<HTMLInputElement>) => { setIsTOSAccepted(event.target.checked); }} defaultChecked={ ...

Issues with implementing routing children in Angular 8

Currently, I am in the process of building a website and facing some issues with implementing the admin section. Within my admin module, I have various components such as login, dashboard, products, etc. However, I am encountering an issue where the childr ...

Error message from OpenAI GPT-3 API: "openai.completions function not found"

I encountered an issue while trying to execute the test code from a tutorial on building a chat app with GPT-3, ReactJS, and Next.js. The error message I received was: TypeError: openai.completions is not a function This occurred when running the follow ...

Click on the button to generate a PDF report using Internet Explorer 11

After encountering some challenges with printing a PDF report specifically on IE 11, I am reaching out for help. The code snippet below works perfectly in Chrome, but when it comes to IE 11, everything falls apart. Just to provide some context, I am develo ...