Limiting the scope of TypeScript types to specific keys, such as Exact/DeepExact

After coming across this specific query on SO, I wanted to delve into creating an Exact type.

My attempt at implementing something akin to a DeepExact seems to be close:

// Desired type that should only accept Exact versions of
type Opts = {
  firstName?: string;
  parent?: string;
  children?: { firstName?: string }[];
};

// Incoming type with unknown fields that should trigger rejections
type Args = {
  firstName?: string;
  parentId?: string;
  children?: { firstName?: string; unknownField?: string }[];
};

const a: Args = null!;
// Compiles as intended, but aiming for parentId and children.unknownField to cause type failures
const o: Opts = a;

// Enforcing only the exact Opts type through this function
function onlyOpts<O>(o: Exact<Opts, O>): void {}

// Examples distinguishing known from unknown fields
const g1 = { firstName: "f" };
onlyOpts(g1);
const g2 = { parent: "p", children: [{ firstName: "c1" }] };
onlyOpts(g2);

// Failing examples involving unknown fields
const b1 = { firstName: "f", parentId: undefined };
onlyOpts(b1); // Correctly fails due to parentId being `never`

const b2 = { firstName: "f", children: [{ firstName: "c1", unknownField: "c1" }] };
onlyOpts(b2); // Should fail but does not, where unknownField still remains `string`

// Demonstrating that B2["children"][]["unknownField"] is still a string
type B2 = Exact<Opts, typeof b2>;

// Prototype of Exact/DeepExact
type Exact<T, U> = T &
  {
    [K in keyof U]: K extends keyof T
      ? T[K] extends Array<infer TU>
        ? U[K] extends Array<infer UU>
          ? Array<Exact<TU, UU>>
          : never
        : U[K]
      : never;
  };

Despite my efforts, the example involving b2 is not failing in the deep/recursive aspect. The type B2 retains children with unknownField: string instead of unknownField: never.

I had expected isolating TU and UU for separate inference, followed by recursion within Exact would resolve the issue, but it hasn't.

Any insights or suggestions?

Answer №1

So far, this solution has been effective for the majority of my basic test cases:

export type Exact<T, U> = T &
  {
    [K in keyof U]: K extends keyof T
      ? T[K] extends Array<infer TU> | undefined | null
        ? U[K] extends Array<infer UU> | undefined | null
          ? Array<Exact<TU, UU>> | undefined | null
          : never
        : U[K]
      : never;
  };

However, it encounters issues when applied to a more complex application with unconventional generic types and usages. I am continuing to refine it, but I have made progress towards addressing the original question.

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

Display Module within Component using Angular 5

In the application I'm working on, I want to incorporate a variety of progress-loader-animations such as spinners or bars. To achieve this, I've developed a module with a component. Now, I'm trying to figure out how to display the module&ap ...

The TypeScript class for Date has a property that outputs a string

In my TypeScript code, I have defined a model class as follows: export class Season { ID: number; Start: Date; } Below is an example of how this model class is utilized within a component: export class SeasonsComponent { seasons: Season[]; sele ...

Angular2: Ensuring Sequential Execution Line by Line - A Comprehensive Guide

I have a designed an Angular2 Navbar Component that features a logout button: import { Component, OnInit } from '@angular/core'; import { LoginService } from '../login.service'; import { Router } from '@angular/router'; @Co ...

The functionality of Angular 4's ngStyle sum operates as a string instead of a number

<div class="scroll-element-content" [ngStyle]="{'width.px': (this.width + this.trackWidth)}"> With this.width set to 400 and this.trackWidth set to 8: The combined width of .scroll-element-content will be 4008 (since the sum is treated as ...

It appears that Type 'MenuItemsProps' does not contain a property named 'map'. This might be causing the error message 'Property 'map' does not exist on

Recently, I delved into learning TypeScript and decided to convert my React code into TypeScript. However, I encountered an issue that left me stumped. I tried passing a state through props to a component with a defined value, hoping that the state would b ...

Refreshing Custom Functions within Excel Add-On - Web Edition

Currently, I am working on an Excel Add-In that includes custom functions utilizing the Javascript API. I have been following a particular tutorial for guidance. While attempting to debug using the Web version of Excel due to its superior logging capabili ...

React Query mutation encountered a TS2554 error: Type argument was not valid

I'm a beginner when it comes to TypeScript and I am using react-query. I tried using mutate, but it is causing an error. TS2554: Expected 1-2 arguments, but got 3. interface: interface ChangePassword{ email: string; password: string; conf ...

"Encountered a TypeScript error (2345) when using customElements.define() in Lit 2

When we upgraded various lit-Components to version 2.1.1 "lit": "2.1.1", a TypeScript error surfaced: The argument 'typeof MyComponent' cannot be assigned to a parameter of type 'CustomElementConstructor'. The &apo ...

In fact, retrieve the file from an S3 bucket and save it to your local

I've been attempting to retrieve an s3 file from my bucket using this function: async Export() { const myKey = '...key...' const mySecret = '...secret...' AWS.config.update( { accessKeyId: myKey, secretAcces ...

JavaScript - Cannot access the 'name' property on an empty object

I am currently following a React tutorial that demonstrates how to create a useForm hook for linking form input to state. Here is the implementation of the hook: const useForm = (initial = {}) => { const [inputs, setInputs] = useState(initial) ...

Leverage Custom_Pipe within TS

I am currently working with a pipe that I have created. Here is the code: @Pipe({ name: 'searchNomES' }) export class SearchNomESPipe implements PipeTransform { transform(uos: IUo[], name?: string,): IUo[] { if (!uos) return []; if (!name) ret ...

Issue: (SystemJS) Unable to find solutions for all parameters in $WebSocket: ([object Object], [object Object], ?)

Upon running the code snippet below, an error is thrown: Error: (SystemJS) Can't resolve all parameters for $WebSocket: ([object Object], [object Object], ?). app.component.ts import { Component } from '@angular/core'; import {$WebSocket} ...

What is the most effective method for dividing a string in TypeScript?

Here is the scenario: receiving a string input that looks like Input text: string = "today lunch 200 #hotelname" Output subject: "today lunch" price: 200 tag: #hotelname My initial solution looks like this: text: string = "today lunch 200 #hotelname" ...

Translating from a higher-level programming language to a lower-level programming language

Is compilation effectively the transformation of high-level programming languages (HLL) into machine code or low-level language? If so, why is TypeScript (a HLL) compiled to JavaScript (also a HLL) instead of being compiled to a low-level language? ...

React Type Mutation response feedback is a valuable tool for receiving input

I am facing an issue with passing the mutation success response in my code. I have a file named change-email.tsx which calls a component file updateEmail.tsx containing a mutation function. The submit function is working fine, but I cannot figure out how t ...

Discovering the generic parameter types with union in TypescriptUncover the

I've been struggling with the code snippets below for a while. Can someone explain why e4 is defined as string and not String? type PropConstructor4<T = any> = { new(...args: any[]): (T & object) } | { (): T } type e4 = StringConstructor ext ...

Running cy.task after all test suites can be done by adding the task in a

I need some guidance on running cy.task after executing all test suites. I have a file generated at the start of the tests that I would like to remove once they are completed. Regardless of whether any tests passed or failed, I want to trigger cy.task im ...

Angular 7 - Implementing periodic JSON data retrieval from server and maintaining local storage within Angular application

Seeking guidance on how to handle updating a static json file stored in the assets directory in an Angular 7 project. The goal is to periodically fetch a json from a server, check for updates, and perform post-processing on the data in the static file (ess ...

Guide on invoking child components' functions from the parent component in Angular 6

Main Component import { Component } from '@angular/core'; import { DisplayComponent } from './display.component'; @Component({ selector: 'my-app', template: ` <button (click)="submit()">Call Child Com ...

Developing Angular PWAs with a focus on microfrontends

I have set up multiple microfrontends using an "app-shell" type of application for the domain root, with each microfrontend on the first path element. Each app is constructed as a standalone angular application utilizing shared libraries to reuse common co ...