Can we guarantee that the key and its corresponding value are both identical strings in Typescript?

Is it possible to enforce the key of a Record to be the same as the inner name value in a large dataset?

interface Person<T> {
  name: T
  title: string
  description: string
}

type People = Record<string, Person<string>>

// example data

const data: People = {
  sarah: {
    name: 'sarah',
    title: 'Sarah',
    description: 'Hello',
  }
}

const badData: People = {
  adam: {
    name: 'john', // This would cause a typescript error ideally.
    ...
  }
}

The attempt to set up People with a generic resulted in having to add all keys to a union type, which is not ideal. The keyof method also did not work as the object wasn't defined where it was needed.

type People<T> = Record<T, Person<T>>

const people: People<keyof typeof people> = {} // Block-scoped variable 'people' used before its declaration.ts(2448)

Answer №1

To achieve this, it is necessary to utilize an additional function.

interface Person<T> {
    name: T
    title: string
    description: string
}

type People = Record<string, Person<string>>


type Validate<T extends People> = {
    [Name in keyof T]: Name extends T[Name]['name'] ? T[Name] : T[Name] & { name: never }
}


const validator = <
    Name extends string,
    Human extends Person<Name>,
    Data extends Record<Name, Human>
>(data: Validate<Data>) => data

const result = validator({
    sarah: {
        name: 'sarah',
        title: 'Sarah',
        description: 'Hello',
    },
    adam: {
        name: 'john', // Error.
        title: 'Sarah',
        description: 'Hello',
    }
})

Playground

Validate goes through each object key/Name and verifies if the top-level name matches Object[Name]['name']. If it does, it returns the same nested object. If not, it returns the same nested object but with the name property overridden as never.

Therefore, the error you encounter points out the area that needs fixing.

If you're interested in Type Inference for function arguments, you may want to read my article

A more generalized version of Validation from @Konrad Madej:

type Validate<Data extends People> = {
    [Name in keyof Data]: Name extends Data[Name]['name'] ? Data[Name] : Omit<Data[Name], 'name'> & { name: Name }
}

If you have numeric keys, please refer to this example:

interface PayloadObj<T> {
    elementId: T;
    values: any;
}

type Payload = Record<number, PayloadObj<number>>;

type Validate<T extends Payload> = {
    [Key in keyof T]: T[Key] & { elementId: Key }
};

const validator = <
    ElementId extends number,
    PayloadValue extends PayloadObj<ElementId>,
    Data extends Record<ElementId, PayloadValue>
>(data: Validate<Data> & Data): Payload => data;

const result = validator({
    0: {
        elementId: 0,
        values: 'Hello',
    },
    1: {
        elementId: 2, // Error.
        values: 'World',
    },
});

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

Exploring Function Type in TypeScript: Utilizing both fat arrow and object literal type

Currently delving into the world of Typescript, I came across two methods for defining function types: using a fat arrow or an object literal. Here's an example: let myAdd1: (x: number, y: number) => number = function(x: number, y: number): n ...

I am looking for a guideline that permits me to restrict the use of a form validation tool

We have developed our own version of the Validators.required form-validator that comes with Angular 7, but now we need to switch to using CustomValidators.required. To enforce this change, we are considering banning the use of the old Validators.required b ...

Match and populate objects from the array with corresponding items

Currently, I have an array and object containing items, and my goal is to check each item in the array to see if its path matches any of the object names. If a match is found, I push it into that object's array. While this part is working fine, I am ...

React-file-viewer shrinks any document to a compact size

I have been searching extensively for information on how to adjust file sizing in react-file-viewer without any success. My objective is to utilize the react-file-viewer to allow users to click on a filename hyperlink and open the file (be it an image, do ...

How to Turn Off GridToolbarExport Menu in React Mui DataGrid

Can someone assist me in disabling the menu in GridToolbarExport? This is how my MUI Data Grid is set up: <DataGrid localeText={{ toolbarExport: "Export as CSV", }} disableColumnMenu={true} components={{ Toolbar ...

Tips for transferring data when clicking in Angular 5 from the parent component to the child component

I need assistance with passing data from a parent component to a child component in Angular 5. I want the child component to render as a separate page instead of within the parent's template. For example, let's say my child component is called & ...

When attempting to compile my Angular project using the command ng build --prod, I encountered a server error stating that the document

Everything was running smoothly when I was working on my local machine, but once I uploaded the files generated from ng build --prod to the server, a problem arose. Now, whenever I try to route via a button in my components, an error appears. When I clic ...

Deactivating Google Map Clustering for Individual Markers

I'm currently utilizing Angular 4, Google Maps v3, and Marker Clusterer v2 - all of which are the latest versions. I'm attempting to implement a straightforward example found in the official Google Maps documentation (https://developers.google.co ...

Utilizing Generic Types in React TypeScript Functional Components

I'm currently developing a TypeScript React component that includes generic type props, so I define the React component as: export interface MyCompProps<T> { items: T[]; labelFunction: (T) => string; iconFunction: (T) => JSX.Element; ...

Unable to clear all checkboxes after deleting

In my application, there are 3 checkboxes along with a master checkbox that allows users to select or deselect all of them at once. Everything works fine with the master checkbox until I delete some rows from the table. After deleting data, I can check th ...

Immutable parameter in constructor

While analyzing some TypeScript code, I stumbled upon a peculiar declaration within a class definition: constructor(readonly constructorParam : Type) { // no assignment of constructorParam here } Surprisingly, constructorParam is still being used as usu ...

Is the Typescript index signature limited to only working with the `any` type?

I recently made changes to my interface and class structure: export interface State { arr : any[]; } export const INITIAL_STATE: State = { arr: [] }; This updated code compiles without any issues. Now, I decided to modify the Interface to incl ...

A guide on implementing isomorphic types in TypeScript

Consider the following scenario with two files, file.ts: let a: Message = "foo" let b: Message = "bar" let c: Message = "baz" Now let's introduce a second file, file2.ts type Message = string function fun(abc: Message): void { } When using functi ...

Adding a new line in the configurations of MatDialogConfig (Angular)

Here is a code snippet: private mDialog: MatDialog, const dialog = new MatDialogConfig(); msg = "I enjoy coding in Angular.\r\n I am learning TypeScript." dialog.data = { message:msg }; alert (msg); mDialog.open(AB ...

Modifying the user interface (UI) through the storage of data in a class variable has proven to be

If I need to update my UI, I can directly pass the data like this: Using HTML Template <li *ngFor="let post of posts; let i = index;"> {{i+1}}) {{post.name}} <button (click)="editCategory(post)" class="btn btn-danger btn-sm">Edit</butto ...

What classification should be given to children when they consist solely of React components?

I'm encountering an issue where I need to access children's props in react using typescript. Every time I attempt to do so, I am faced with the following error message: Property 'props' does not exist on type 'string | number | boo ...

Searching and adding new elements to a sorted array of objects using binary insertion algorithm

I'm currently working on implementing a method to insert an object into a sorted array using binary search to determine the correct index for the new object. You can view the code on codesanbox The array I have is sorted using the following comparis ...

Generating typescript definitions for Polymer 2.4 packages

According to information provided in the latest announcement, declarations are now automatically generated from the Polymer source. I recently upgraded to Polymer 2.4 and encountered an error during project build due to a missing typescript definition fil ...

Angular 6 provides a regular expression that specifically targets the removal of any characters that are not numbers and enforces the allowance of

I have tried various solutions to restrict input in an Angular material input box, but none seem to be effective for my specific case. I need the input field to only allow numbers and a decimal point. Any other characters should be automatically removed as ...

Discovering the Object with the Lowest Key Value in Typescript

Within my TypeScript code, I have defined a List as myList: Package[]. The structure of the Package model is outlined below - export class Package { ID: Number; Price: Number; } I am trying to retrieve a Package object with the lowest Price value using ...