How come type {} (non-nullish values) can be assigned to subtypes such as { [key: string]: string }?

When using {}, it specifically refers to non-nullish values; it does not include objects with no properties. However, there was an interesting observation:

const x: {} = 0; // This is valid because 0 is a non-nullish value
const y: { [key: string]: string } = 0; // This gives an error as 0 is not of type { [key: string]: string }
const z: { [key: string]: string } = x; // Surprisingly, this assignment passes without errors

One might expect TypeScript to generate an error for the assignment of z, similar to how it does for the assignment of y. However, it seems to be allowing it without any issues. Is this a bug?

Answer №1

This demonstrates a key aspect of TypeScript: assignability is not always transitive. Despite the general expectation of assignability being transitive, there are scenarios where this rule does not hold. For instance, consider types A, B, and C such that A extends B and B extends C, yet A extends C is false. This deviation from transitivity can lead to unexpected results within TypeScript due to its type system's limitations on soundness.


In a specific case like

const x: {} = 0;
, the assignment is allowed because the empty object type {} has no known properties that conflict with those of 0. On the other hand,
const y: { [key: string]: string } = 0;
is disallowed as not every property of 0 is assignable to string.

Moreover,

const z: { [key: string]: string } = x;
is permitted since the anonymous empty object type {} receives an implicit index signature. While interfaces do not inherently receive implicit index signatures, non-interface object types exhibit characteristics of object literal types, enabling such assignments.


Though these behaviors may seem unsound, they align with TypeScript's design choices to balance safety and productivity for developers. Implicit index signatures offer convenience in coding practices despite potential conflicts with actual object properties matching index signatures. While some may challenge these decisions, TypeScript functions as intended in the examples discussed here, reflecting deliberate design rather than bugs.

Answer №2

In my opinion, this issue may not be considered a bug because when you use the following code:

const z: { [key: string]: string } = x;

The compiler does not actually check the content of variable x but instead checks its type, which can potentially match. In contrast to this situation:

const y: { [key: string]: string } = 0;

where the compiler immediately recognizes that the types do not align.

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

MUI options - The specified type 'string' cannot be matched with type '"icon" | "iconOnly" | "text" | "outlined" | "contained" | undefined'

Is it possible to utilize custom variants in MUI v5? I am having trouble using a custom variant according to their documentation: https://mui.com/material-ui/customization/theme-components/#creating-new-component-variants declare module "@mui/material ...

Is there a way to utilize a nearby directory as a dependency for a separate Typescript project?

I am working with a repository that has the following structure in typescript: . ├── common ├── project_1 └── project_2 My goal is to have the common package be used by both project_1 and project_2 as a local dependency. I am looking for ...

Establishing the initial state within the constructor of a React Component utilizing a generic state

I have encountered an issue with React and Typescript. I am working on a component that utilizes two generics for props and state. interface Props { foo: string; } interface State { bar: string; } class Foo< P extends Props = Props, S e ...

Issue arises in Nextjs14 when utilizing <Link> to navigate to the same page, resulting in Loading.tsx malfunction

In my Next.js project using version 14.2.7, the root page is a server component that retrieves a ticket from a server action. import { uniqueRandom } from "lodash"; import Link from "next/link"; const getTicket = () => new Promise&l ...

What is the best way to save code snippets in Strapi for easy integration with SSG NextJS?

While I realize this may not be the typical scenario, please listen to my situation: I am using Strapi and creating components and collections. One of these collections needs to include code snippets (specifically typescript) that I have stored in a GitH ...

Retrieving the key from an object using an indexed signature in Typescript

I have a TypeScript module where I am importing a specific type and function: type Attributes = { [key: string]: number; }; function Fn<KeysOfAttributes extends string>(opts: { attributes: Attributes }): any { // ... } Unfortunately, I am unab ...

Tips for implementing absolute import paths in a library project

In my workspace, I have a library with two projects: one for the library itself and another for a test application. ├── projects    ├── midi-app    └── midi-lib Within the workspace's tsconfig.json file, I set up paths for @a ...

Next JS now includes the option to add the async attribute when generating a list of script files

We are currently working on a nextJs application and are looking to add asynchronous functionality to all existing script tags. Despite numerous attempts, we haven't been successful in achieving this. Can anyone provide some guidance or assistance? &l ...

Storing an image in MongoDB using Multer as a file from Angular is not working as anticipated

I'm currently dealing with an issue that I believe is not functioning correctly. I installed a library in Angular called cropper.js from https://github.com/matheusdavidson/angular-cropperjs. The frontend code provided by the developer utilizes this li ...

How to ensure attribute/property wrapping within max line length in Visual Studio Code for code formatting

Although my question may seem familiar, I have yet to find a solution after reading through numerous similar queries. For my project development, I utilize Visual Studio Code with Angular, Typescript, and external libraries. With many lengthy lines of HTM ...

Could you confirm if this is a TypeScript function?

Recently, while delving into the vue-next source code, I stumbled upon a particular line that left me puzzled. Due to my limited experience with TypeScript, I found myself struggling to grasp its purpose. Could someone clarify if this snippet constitutes ...

Maintain the selected list item active using ngFor after it is clicked

I am currently working with Angular and Typescript, and I have the following HTML code snippet. However, I am facing an issue where the items are not staying active after being clicked. <li *ngFor="let permission of tempPermission" class ...

Most effective method for filling in nested information

Let me start by admitting that I've delved deep into this issue, possibly missing a simpler solution along the way. If there is an obvious solution staring me in the face, I apologize! Here's the problem at hand: I'm working with a set of ...

Using multiple pipes in Angular 4 with Typescript to manipulate values

I've developed a custom pipe called 'filterBy' and I want to implement it in my application. Most of it is functioning well, but I am facing challenges when trying to use this pipe for multiple properties. Allow me to elaborate on what I am ...

Dealing with the "this" problem in TypeScript and its impact on scope

Here is my code snippet: class MyClass { name = "MyClass"; // traditional method definition getName1(){ return this.name; } // method defined as an arrow function getName2 = () => { return this.name; ...

Unspecified properties emerge post-Angular update

We recently consolidated multiple Angular 16 projects into one NX mono repository using Angular 17. Everything is functioning properly, EXCEPT we have noticed a peculiar change in behavior with our models. Previously, unset properties were simply not displ ...

Angular 2 along with Typescript compiler duplicates HTML and CSS documents

When working with Angular2, I usually set the following configuration in my tsconfig.json file: "outDir": "dist/app" This setup allows for the transpiled .js and .map files to be generated within the /dist/app/ folder or its subfolders. This typi ...

Having trouble finding the module path for 'chartjs-plugin-stacked100'

Currently, I am diving into using chart.js in conjunction with react-chart-js-2. Within my package.json, you'll find the following dependencies: "dependencies": { "chart.js": "^3.6.0", "chartjs-plugin ...

How can the radio button's checked property be bound to a boolean attribute of a component in Angular 2?

Is there a way to connect the checked property of a radio button to a boolean variable in a Component class? I would like the radio button in the template to be pre-selected if the boolean flag is true. I attempted to use <input type="radio" [(ngModel) ...

Creating a hyperlink to a subdomain in TypeScript Execution File

I am seeking to add a link directing to a subdomain in the navigation bar of a react theme. Although I feel a bit hesitant about asking for help on something seemingly simple, I have struggled to find any relevant examples online to assist me. Below is the ...