What sets apart Records and Objects in TypeScript?

I am exploring how to define a conditional type in TypeScript that can differentiate between "record" types (with dynamic/unbounded keys) and plain object types (with predefined keys).

type R = { [x: string]: any }
type O = { a: string; b: number }

One approach is to extract the keys of the types and then verify using a nonexistent property:

type R = { [x: string]: any }
type O = { a: string; b: number }

type IsRecord<T> = '__NEVER__' extends keyof T ? true : false

type RR = IsRecord<R> // true
type OO = IsRecord<O> // false

However, this method feels somewhat hacky. Is there a cleaner solution for achieving this distinction?

Answer №1

When determining if a type is indexable, you can check if the key includes the type itself:

type IsRecord<T> = string extends keyof T ? true : false

type RR = IsRecord<R> // true
type OO = IsRecord<O> // false

Instead of using a fake key, consider using string to avoid any perceived "hackiness."

It's worth noting that there are types with number-indexes (like Array<any>), which won't be identified as records since string isn't necessarily a key in a number-indexed type:

type N = { [x: number]: any };
type NN = IsRecord<N>; // false

If you want to identify both string and number indexes as record-like, checking for number is sufficient since, from a key perspective, number is considered assignable to string. This might seem confusing, but JS objects ultimately use string or symbol keys, treating all string-indexed types as having both string and number keys. TypeScript supports this behavior:

type IsNumericIndex<T> = number extends keyof T ? true : false

type RRR = IsNumericIndex<R> // true
type OOO = IsNumericIndex<O> // false
type NNN = IsNumericIndex<N> // true

Check out this code on Playground

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

Testing the API client against a remote API with constantly changing endpoints

Imagine I'm developing an API client for a complex API that is constantly changing and unreliable. I want to test this client using Jest, but I prefer to test it against a snapshot of the API response rather than the live API. However, I don't wa ...

Inner output not displaying on the screen

Based on the outcome of the graphql query, certain items are generated within the function contact export const Whitelist: React.FunctionComponent = (props) => { const [userData, setUserData] = useState<UsersQueryHookResult>(''); c ...

The onChange function in React is not behaving as I anticipated

Consider the following data structure for tables: an array of objects containing nested objects: [ { "1": { "1": "Item s ", "2": "Manufacturer ", "3" ...

Converting a generic object from snake_case to camelCase using TypeScript

Looking to create a function that can transform an object with snake case keys into one with camel case keys. How can this be achieved in TypeScript by typing the input object, but keeping the solution generic? type InputType = { snake_case_key_1: numbe ...

Beating the API call system: Utilizing the RxJS skip operator

Currently, I am attempting to utilize the skip operator in RxJS to skip the initial API call. However, despite my efforts, I have not been successful in achieving this. const source = of('a', 'b', 'c', 'd', 'e&a ...

Encountering a NextJS _app.tsx problem - error - Issue with ./pages/_app.tsx file: line 3

Having trouble creating a custom script for my NextJs Project. Here's the error log: Error - ./pages/_app.tsx:3:12 Syntax error: Unexpected token, expected "from" 1 | import React from 'react' 2 | import '../styles/globals.css&apos ...

A reference to 'this' is not permissible within a static function in Types

Based on this GitHub issue, it is stated that referencing this in a static context is allowed. However, when using a class structure like the following: class ZController { static async b(req: RequestType, res: Response) { await this.a(req) ...

Object autofill - Typescript utilizing Angular 5

I am working with an object called "features." Here is an example of the object: [{"_id":"5af03d95c4c18d16255b5ac7","name":"Feature 1","description":"<p>Description</p>\n","neworchange":"new","releaseId":"5aead2d6b28715733166e59a","produc ...

Establish a many-to-many relationship in Prisma where one of the fields is sourced from a separate table

I'm currently working with a Prisma schema that includes products, orders, and a many-to-many relationship between them. My goal is to store the product price in the relation table so that I can capture the price of the product at the time of sale, re ...

Encountering difficulties when attempting to load a module with the "js" extension in a TypeScript environment

When making a GET request with Systemjs, the extension .js is not being added to the URL. These are my TypeScript Classes customer.ts import {Address} from "./Address"; export class Customer { private _customerName: string = ""; public Customer ...

The call to 'setRequestHeader' on 'XMLHttpRequest' was unsuccessful due to the object's state not being OPENED

While developing an angular application with a restful API get(), I encountered a few errors such as unauthorization error:401 which I managed to resolve. However, now I am facing another error that seems quite straightforward. I even tried adding the CORS ...

Prohibit the use of screen printing in a modern web application developed with Angular or ReactJS

Is there a way to prevent screen capturing and recording, specifically in regards to progressive web apps? While some mobile applications offer this feature, I am curious about the feasibility of implementing it in progressive web apps without requiring an ...

Building custom components in Ionic 4

What is the best way to integrate a newly created component in Ionic 4 after executing ionic g component myComponent? I am looking to incorporate this custom component into my home page. ...

Encountering a glitch while integrating the angular-select2 module into an Ionic 3 application

Attempting to integrate the angular-select2 npm module into my ionic 3 app. Successfully installed the module using npm within my project Imported the package into my app.module.ts file Added <select2> tags into the html file of my application Enc ...

Compiling TypeScript code with Electron

Every time I attempt to compile a TypeScript Electron project sample, I encounter the issue 'chrome' does not exist on type ProcessVersions. Despite the Electron website suggesting that including the Electron node_module should provide TypeScript ...

"Error: The term 'Readable' is not

When I input this code: const { Readable } = require('stream'); Readable ends up being undefined. However, when I try this code instead: import { Readable } from 'stream'; Readable becomes an empty object with no properties. It&apos ...

What is the solution to the TypeScript error stating that there is no index signature with a parameter of type 'string' on the 'Object' type?

While working on an Angular project, I came across an issue when writing a Typescript function for a service. The error message stated: "Element implicitly has an 'any' type because expression of type 'string' can't be used to inde ...

SVG: What could be causing the shapes inside the svg to not be visible when overlaid on an image?

I am currently working on a project using Angular where my objective is to create a polygon within an image. In order to achieve this, I have placed an SVG element inside the image tag. Both the image and the SVG have fixed width and height dimensions, and ...

Applying REGEX on input text in React Native

I'm having trouble getting my regex function to work correctly, so I believe there might be an error in my code. Any assistance would be greatly appreciated. Here is the regex function I am using: let validatePlate = (plate) => { var re = /(^[A ...

Error: The function call does not match any of the overloads. 'VueRouter' is not recognized

I'm new to TypeScript and currently trying to implement vue-router in my project. Encountering the following errors: Error TS2769: No overload matches this call in source\app\main.ts(3,3). Overload 1 of 3, '(options?: ThisTypedCompon ...