Custom mapped type results in intermediate forms

I've recently developed a type in Typescript that explicitly blocks specific properties from objects/interfaces. This is necessary because Typescript's excess property checking only kicks in when an object literal is directly assigned, not when it's first assigned to a variable.

For more information on "Excess Property Checks" check out this link: https://www.typescriptlang.org/docs/handbook/interfaces.html

I've encountered situations where I need functions to accept objects without certain predefined properties. While passing in an object literal can solve this issue, there's room for error if the next developer misses this detail. Hence, I decided to completely block these properties (I understand the importance of runtime checks for excess props, but my focus here is on TS).

Mainly, I'm wondering if it's possible to create new types like DisallowBandC based on the original Disallow type?

Furthermore, I'm curious if it's feasible to implement Disallow without forming unions between two types. Any suggestions for simplification are also welcome.

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

type Never<T, K extends keyof T> = { readonly [P in K]?: never };

// Is it achievable without using unions?
type Disallow<T, K extends keyof T> = Omit<T, K> & Never<T, K>;

interface Stuff {
  readonly a: string;
  readonly b?: number;
  readonly c: string | null;
  readonly d: null;
  readonly e?: null;
  readonly f?: undefined;
  readonly g: string;
}

type Blocked = 'b' | 'c'

// This setup works fine
export type Disallowed = Disallow<Stuff, Blocked>;

// However, this one does not:
export type DisallowBandC<T> = Disallow<T, Blocked>;

// Receiving TS Error:
// "Type 'Blocked' does not satisfy the constraint 'keyof T'.
//  Type '"b"' is not assignable to type 'keyof T'."

Answer №1

You have encountered a restriction where the values of K need to be keys within T. To remove this limitation, you can implement the following adjustments:

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

// Eliminate the requirement for T in this scenario
type Never<K extends keyof any> = { readonly [P in K]?: never };

// Remove the constraint of restricting K to T's keys
type Disallow<T, K extends keyof any> = Omit<T, K> & Never<K>;

Now the solution works as intended:

export type DisallowBandC<T> = Disallow<T, Blocked>;

By lifting the constraint K extends keyof T, you now have the freedom to define a T without any Blocked keys at all:

type IsThisOkay = DisallowBandC<{foo: string}>; 
// equivalent to {foo: string, b?: never, c?: never}

I assume that aligns with your requirements, correct?


In addressing your second query about representing Disallow without using an intersection (&) and only allowing a union (

|</code), it is indeed possible if you aim for the final output not to include an intersection but still incorporate it in the definition:</p>

<pre><code>type Disallow<T, K extends keyof any> = {
  [P in keyof (Omit<T, K> & Never<K>)]: P extends K ? never : P extends keyof T ? T[P] : never
};

Alternatively, just to emphasize the use of intersections in the definition:

type Disallow<T, K extends keyof any> = {
  [P in keyof (Omit<T, K> & Never<K>)]: (Omit<T, K> & Never<K>)[P]
}

This essentially achieves the same outcome while being a mapped type that iterates through the keys of

Omit<T, K> & Never<K>
. Despite appearing similar to K | keyof T, utilizing the intersection ensures TypeScript maintains the original properties such as readonly and optional nature from T. Let's confirm:

type TryItOut = DisallowBandC<Stuff>;

The result indicates:

type TryItOut = {
    readonly a: string;
    readonly d: null;
    readonly e?: null | undefined;
    readonly f?: undefined;
    readonly g: string;
    readonly b?: undefined;
    readonly c?: undefined;
}

Seems like everything is in order. Wishing you the best of luck with your endeavors!

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

How to import a module from the root path using TypeScript in IntelliJ IDEA

Despite this topic being widely discussed, I still struggle to understand it. Below is my tsconfig.json file: { "compilerOptions": { "module": "commonjs", "target": "es2017", "sourceMap": true, "declaration": true, "allowSyntheticDe ...

How to Verify User Login Status with Firebase Auth in Node.js Cloud Functions

My goal is to create an Express dynamic web page on Node.js. I aim to execute the following logic on the server (Firebase Cloud Functions) at path /: If the client is currently logged in (Firebase Auth), the home page my_home_page.html should be rendered ...

Looking to organize a table in jhipster but unsure of the process. Can someone provide guidance on how

For the past week, I have been delving into jhipster and attempting to incorporate a sortable table. Could someone clarify how exactly the jhiSort and jhiSortBy functions operate? I am struggling to grasp the purpose of the predicate, ascending, and call ...

What is the contrast between element.getAttribute() value and a String in protractor?

When using protractor and typescript, I need to verify that the text saved in a textbox matches a certain string by comparing it with the resulting value of element.getAttribute("value"). Unfortunately, getText() does not work for this scenario b ...

Encountering an issue with Nuxt 3.5.1 during the build process: "ERROR Cannot read properties of undefined (reading 'sys') (x4)"

I am currently working on an application built with Nuxt version 3.5.1. Here is a snippet of the script code: <script lang="ts" setup> import { IProduct } from './types'; const p = defineProps<IProduct>(); < ...

Obtain abbreviated names for the days of the week starting from Monday to Sunday using JavaScript

Is there a way to retrieve the abbreviated names of each day of the week in JavaScript, starting from Monday through Sunday? ...

Implementing Angular's Advanced Filtering Across Multiple Data Fields

I am looking to create a custom filter for a list. Here is an example of the Array of Objects: myList: [ { "id": 1, "title":"title", "city":"city name", "types":[ { ...

"Looking to log in with NextAuth's Google Login but can't find the Client Secret

I am attempting to create a login feature using Next Auth. All the necessary access data has been provided in a .env.local file. Below are the details: GOOGLE_CLIENT_ID=[this information should remain private].apps.googleusercontent.com GOOGLE_CLIENT_SECR ...

"Unlocking the treasure trove: Extracting a single item from my Firebase real-time database using

Searching for the user's article in my Realtime database to display information. https://i.sstatic.net/yCdgf.png This is my Ionic script page Below are the imports I have: I am able to retrieve the user's ID, but I'm facing difficulty in ...

What is the correct method for typing a React functional component with properties?

Here's a react functional component I have created... const MyFunction = () => { // lots of logic MyFunction.loaded = someBoolean; return <div>just one line</div> } MyFunction.refresh = () => ....... I added two properti ...

Exploring the New Features of Angular 13 with Typescript, Regular Expressions, and

Currently, I am working on an Angular 13 project and I am trying to create a directive that will only allow users to type numbers and '/' in my date input field format of dd/mm/yyyy. Below is the regular expression (Regx) that I am using: if (!St ...

Implementing Generic Redux Actions in Typescript with Iterable Types

Resolved: I made a mistake by trying to deconstruct an object in Object.assign instead of just passing the object. Thanks to the assistance from @Eldar and @Akxe, I was able to see my error in the comments. Issue: I'm facing a problem with creating a ...

What's the best way to include various type dependencies in a TypeScript project?

Is there a more efficient way to add types for all dependencies in my project without having to do it manually for each one? Perhaps there is a tool or binary I can use to install all types at once based on the dependencies listed in package.json file. I ...

Encountering an issue in app.module.ts with Angular 6 when trying to pass an array of injectables to providers resulting in the error message: "Property 'length' of undefined cannot be read"

Below is an array containing injectables connected to services: import { YouTubeSearchService, YOUTUBE_API_KEY, YOUTUBE_API_URL } from './you-tube.service'; export const youTubeSearchInjectables: Array<any> = [ { provide: Yo ...

Unable to find the locally stored directory in the device's file system using Nativescript file-system

While working on creating an audio file, everything seems to be running smoothly as the recording indicator shows no errors. However, once the app generates the directory, I am unable to locate it in the local storage. The code I am using is: var audioFo ...

What is the best way to implement useAsync (from the react-async-hook library) using TypeScript?

Currently, I am utilizing react-async-hook to retrieve API data within a React component. const popularProducts = useAsync(fetchPopularProducts, []); The fetchPopularProducts() function is an asynchronous method used to make API calls using fetch: export ...

The test session failed to launch due to an error in initializing the "@wdio/cucumber-framework" module. Error message: [ERR_PACKAGE_PATH_NOT_EXPORTED]

I added @wdio/cli to my project using the command 'npm i --save-dev @wdio\cli'. Next, I ran 'npx wdio init' and chose 'cucumber', 'selenium-standalone-service', 'typescript', 'allure' along w ...

The Console.Log function will not run if it is placed within the RXJS Tap operator

In my current setup, I have the following observables: this.authenticationService.isSignedIn() -> Observable<Boolean> this.user$ -> Observable<UserModel> I am in need of checking a condition based on both these observables, so I attempt ...

What could be causing the malfunction of my button's event handlers?

Users can fill out a form in which they provide information about a grocery item they want to add to their cart. After completing the form, they can click on the "Add Item" button. Here's the form in my app.component.html: <form (ngSubmit)="a ...

Display an image on an HTML page based on the TypeScript data in an Ionic Angular application

After retrieving user profile data from the database and storing it in an observable, I am able to access properties such as profileData.username, profileData.msgnumber, and more. When profileData.avatar returns the name of the avatar the user is using, I ...