Ensure that type checking is applied exclusively to the properties of an object, and define the keys as constants

Defining constants with predefined keys in typescript can be confusing at times. Let's walk through an example:

// suppose we have this object
const MY_LIB = {
    id_1: {foo: 'foo', bar: 'bar'},
    id_2: {foo: 'foo', bar: 'bar'}
} as const

// later on, we might want to create a type based on it, like this:
type LibIds = keyof typeof MY_LIB // 'id_1' | 'id_2'

Everything seems fine up to this point.

However, when dealing with large objects containing numerous keys and complex properties (such as nested objects), it becomes necessary to enforce type checking on the library object itself. For example:

// consider the following scenario
const MY_LIB: Record<string, {foo: string, bar: string}> = {
    id_1: {foo: 'foo', bar: 'bar'},
    id_2: {foo: 'foo', bar: 123} // error detected! - bar should be a string
} as const

// but now our type doesn't function properly anymore, it becomes of generic value "string"
type LibIds = keyof typeof MY_LIB // string

My ideal solution would look something like this:

type ReadonlyLibrary<Libtype> = // implementation details go here

const MY_LIB: ReadonlyLibrary<{foo: stringl bar: string}> = {
    id_1: {foo: 'foo', bar: 'bar'},
    id_2: {foo: 'foo', bar: 'bar'}
}
// MY_LIB is of type: {[key: 'id_1' | 'id_2']: {foo: stringl bar: string}}

// this approach aligns perfectly with my requirements
type LibKeys = keyof typeof MY_LIB // 'id1' | 'id_2'

How do you typically handle this situation? Are there any better techniques or strategies?

Answer №1

Utilize the satisfies operator:

const MY_LIB = {
    id_1: {foo: 'foo', bar: 'bar'},
    id_2: {foo: 'foo', bar: 123} // error caught! - bar should be a string
} as const satisfies Record<string, {foo: string, bar: string}>;


type LibIds = keyof typeof MY_LIB // "id_1" | "id_2"

Visit The satisfies Operator

Prior to the introduction of the satisfies operator in TS 4.9, the method was to use a constrained identity function:

function createMyLib <T extends Record<string, {foo: string, bar: string}>>(def: T): T {
  return def;
}
export const MY_LIB = createMyLib({
  id_1: {foo: 'foo', bar: 'bar'},
  id_2: {foo: 'foo', bar: '123'} 
} as const);

type LibIds = keyof typeof MY_LIB;

Answer №2

If you're working with TypeScript 4.9 or later, consider utilizing the satisfies operator introduced in that version. This operator is used to verify if a type matches a specific object while also inferring the type from the object itself.

const MY_LIBRARY = {
  id_1: { foo: 'foo', bar: 'bar' },
  id_2: { foo: 'foo', bar: '123' },
} satisfies Record<string, { foo: string; bar: string }>;

type LibraryIds = keyof typeof MY_LIBRARY; // "id_1" | "id_2"

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

New substitute for extending the extent in OpenLayers 4

I am currently in the process of migrating my code from OpenLayers 3 to OpenLayers 4 using TypeScript. Previously, I had a code snippet that extended the extent of my map so that all vector shapes I drew would be visible upon loading... angular.forEach(w ...

Retrieve the specific type of property from a generic data structure

I am currently working on a project where I need to determine the type of property within a given Type: type FooBarType { foo: string, bar: number } The function would be structured like this: getType<K extends keyof T>(key: K): string. The ...

Struggling to integrate authentication and authorization features into a ReactJS application with Microsoft Azure AD or Database login functionality

We have an application built on React v18 with a backend that includes a Web API and SQL Server database. Our requirement is to authenticate and authorize users using either MS Azure AD or the database. If a user attempts to log in with a username and pas ...

Angular 4 allows you to assign unique colors to each row index in an HTML table

My goal is to dynamically change the colors of selected rows every time a button outside the table is clicked. I am currently utilizing the latest version of Angular. While I am familiar with setting row colors using CSS, I am uncertain about how to manip ...

How to Pass a JSON Object to a Child Component in Angular and Display It Without Showing "[Object

Need help with my API call implementation. Here's a snippet from my Input component: Input.html <form (submit)="getTransactions()"> <div class="form-group"> <label for="exampleInputEmail1"></label> <input type="t ...

Tips for adjusting card content to fit its size in material ui?

I'm looking to implement Material UI cards in a grid layout, each containing a Highcharts chart as shown in this demo. However, I'm facing an issue where the content does not adjust properly when the card size is fixed. How can I resolve this? A ...

Vue Deep Watcher fails to activate when the data is altered

While the countdown timer is functioning properly, it seems that the deep watcher is not working as expected. Despite attempting to log the new value of seconds in the console, it does not display anything even though the countdown timer continues to funct ...

Save Component Characteristics in a type-safe array

Is it possible in Svelte to define a strongly typed array that matches the properties exported by a specific component? For instance, if I have the following code snippet, const things = [], is there a way for Svelte to recognize that each item within the ...

Exploring Opencascade.js: Uncovering the Real Text within a TCollection_ExtendedString

I am currently attempting to retrieve the name of an assembly part that I have extracted from a .step file. My method is inspired by a blog post found at , however, I am implementing it using javascript. I have managed to extract the TDataStd_Name attribut ...

Monitoring URL changes in Angular2 using the HostListener

I have a common navbar component that is included in every page of my website. I would like it to detect when the URL changes using a HostListener. @HostListener('window:hashchange', ['$event']) onHashChange(event) { this.checkCu ...

Choose the Angular 2 option

I am having an issue with the select option in my code. The person's gender property is assigned 'M' for male, but I need to allow users to change it to 'F' for female. Here is the HTML code snippet: <span > <span &g ...

Definition of composed types in TypeScript generics

I'm curious if there is a functional distinction between the two TypeScript type declarations below: object: Observable<number> | Observable<number[]> object: Observable<number | number[]> If there is a difference, what are the ...

When the appdir is utilized, the subsequent export process encounters a failure with the error message "PageNotFoundError: Module for page /(...) not

I have implemented NextJS with the experimental appDir flag and organized my pages in the following manner: https://i.stack.imgur.com/M7r0k.png My layout.tsx file at the root and onboard look like this: export default function DefaultLayout({ children }) ...

Dealing with observable errors in Angular 2 beta.12 and RxJS 5 beta.3

Greetings, Currently, I am working with Angular2 beta 12 within VS2015. Upon updating from rxjs version 5.0.0-beta.2 to beta.3, I started encountering several exceptions primarily related to promises. For instance: The property map is not present in th ...

How come JSON.parse is altering the data within nested arrays?

In my journey to master Angular 2, I decided to challenge myself by creating a Connect Four game using Angular CLI back when it was still utilizing SystemJS. Now, with the switch to the new Webpack-based CLI, I am encountering a peculiar issue... The fun ...

Issue with Undefined Variable in Angular 2 and Ionic Framework

I included the following code in my HTML file: <ion-col col-3 align="right"> <ion-item> <ion-label>Show as</ion-label> <ion-select [ngModel]="SelectedView" (ngModelChange)="onViewChange($eve ...

Using functional components in Redux without the need for React

I have a functioning component that does not use React, but utilizes Redux as shown below: export const isAuthenticated = () => ({user}) => { console.log("user : ", user); return true; }; const mapStateToProps = (state) => { ...

Struggling with an issue in React and Bootstrap4 while developing an application with create-react-app

In my experience with Windows 10, I encountered two scenarios where I faced problems. The first scenario involved creating applications using the create-react-app command and installing it with npm i -g [email protected]. Scenario 1 I stopped the React s ...

Issue TS2345: Cannot use type 'Product | undefined' as an argument for type 'Product[] | PromiseLike<Product[]>'

Having trouble retrieving my products using their IDs You can find the code here ...

What is the reason for restricting a placeholder for an optional property in the interface to only be of type any?

I am facing a challenge with a file containing a single declaration, which is for an interface: interface NamedPerson { firstName: string; age?: number; [propName: string]: any; greet(lastName: string): void; } Everything works perfectly ...