Ensuring that an interface exclusively contains properties from another interface

If I define an interface like this:

interface Client {
  id: number;
  email: string;
  firstName: string;
  lastName: string;
  cellNumberFull: string;
}

Then, how can I create a new interface that includes only the properties from GoClient?

interface ClientRestricted {
  firstName: string;
  lastName: string;
  cellNumberFull: string;
  foo: string; // This would throw an error
}

I am searching for a way to achieve the opposite of extending an interface. Is there such a feature available?

Answer №1

To enhance your interface, consider extending a mapped conditional type that depends on the interface itself. This is a recursive type definition known as F-bounded quantification, which allows for complex type constraints. Here's an example:

type Restrict<T, U> = { [K in keyof U]: K extends keyof T ? T[K] : never };
type RestrictClient<U> = Restrict<Client, U>;

// Works as desired
interface Okay extends RestrictClient<Okay> {
  firstName: string;
  lastName: string;
  cellNumberFull: string;
}

// Error occurs intentionally
interface Extra extends RestrictClient<Extra> {
  //      ~~~~~
  // Types of property 'foo' are incompatible.
  // Type 'string' is not assignable to type 'never'.
  firstName: string;
  lastName: string;
  cellNumberFull: string;
  foo: string;
}

By defining your new interface as

I extends RestrictClient<I>
, it will only work if I can be assigned to
RestrictClient<I></code. Essentially, this means each key <code>K
in I must exist in Client with the same or narrower type.

This approach leads to the following outcomes:

// Properties can be narrowed
interface Narrowed extends RestrictClient<Narrowed> {
  firstName: "specificString";
}

// Properties cannot be widened
interface Widened extends RestrictClient<Widened> {
  //      ~~~~~~~ <-- number not assignable to string
  firstName: string | number;
}

// Changing properties to unrelated types results in error
interface Unrelated extends RestrictClient<Unrelated> {
  //      ~~~~~~~~~ <-- number not assignable to string
  firstName: number;
}

If this solution doesn't align perfectly with your requirements, you might need to adjust the definition of Restrict for a closer match. Nevertheless, I hope this provides some valuable insights for your project. Good luck!

Link to code

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

Original: Generic for type guard functionRewritten: Universal

I have successfully created a function that filters a list of two types into two separate lists of unique type using hardcoded types: interface TypeA { kind: 'typeA'; } interface TypeB { kind: 'typeB'; } filterMixedList(mixedList$: ...

I am unable to utilize NavLink unless it is within the confines of the <Router> component

I am currently developing a React application using react-router-dom. To enhance the user experience with smooth transitions, I integrated framer-motion for page animations. However, I encountered an issue where my navbar was being animated and recreated e ...

What methods exist for creating visual representations of data from a table without relying on plotting libraries?

Is there a way to plot graphs directly from a Data Table without the need for external graph libraries like plotly or highcharts? Ideally, I am looking for a solution similar to ag-grid where the functionality comes built-in without requiring manual code ...

Merging an unspecified number of observables in Rxjs

My latest project involves creating a custom loader for @ngx-translate. The loader is designed to fetch multiple JSON translation files from a specific directory based on the chosen language. Currently, I am implementing file loading through an index.json ...

Strategies for Refreshing a Component After Modifying Data in the Store

Having trouble updating my table component when the data in my store changes. I have a simple table using v-for as shown below: <tr v-for="d in getDatas" v-bind:key="d.id"> and buttons to navigate between pages: <button class= ...

The concept of recursive generics in combination with array inference

I'm struggling to develop a couple of generic recursive types to adjust the structure of existing types. I can't figure out why the sections detecting arrays and nested objects are not being activated. Any thoughts on what might be going wrong? ...

Angular 12 Directive causing NG-SELECT disabled feature to malfunction

Looking for a way to disable ng-select using a directive. Does anyone have any suggestions on how to accomplish this? Here is the code I have been working with, along with an example that I was trying to implement. setTimeout(() => { const selectElem ...

Utilizing the spread operator in Typescript interfaces: best practices

I have a react component that includes the spread operator operating on ...other and passed down to lower levels of the component. interface ButtonProps { colourMode: string; regular: boolean; buttonText: string; disabled?: boolean; iconSize?: st ...

What steps should be taken to resolve the error message "This Expression is not constructable"?

I'm trying to import a JavaScript class into TypeScript, but I keep getting the error message This expression is not constructable.. The TypeScript compiler also indicates that A does not have a constructor signature. Can anyone help me figure out how ...

parsing objects within an HTML component in Angular

Is there a way to utilize an object as the @input parameter in HTML? For example: <app-home [param]="user.salary"></app-home> However, the type of my user object is structured like this: user:Person=new Employee(); The classes invol ...

Encountering an issue during the initialization of the Google Passportjs

I recently made the switch from JavaScript to TypeScript in my server project and I'm currently tidying up some code. I decided to combine my Google Passport OAuth stuff and login routes into a single file, but it seems like I've broken something ...

Incorporating a Custom CKEditor5 Build into an Angular Application

I am currently in the process of developing an article editor, utilizing the Angular Integration for CKEditor5. By following the provided documentation, I have successfully implemented the ClassicEditor build with the ckeditor component. Below are the ess ...

How can I resolve a promise that is still pending within the "then" block?

Here is a piece of code that I have written: fetch(`${URL}${PATH}`) .then(res => { const d = res.json(); console.log("The data is: ", d); return d; }) When the code runs, it outputs The data is: Promise { <pending> ...

What is the reason behind taps in TypeScript only registering the first tap event?

One issue I am encountering is that only the first tap works when clicked, and subsequent taps result in an error message: Uncaught TypeError: Cannot read properties of undefined (reading 'classList') Here is the code I am using: https://codepen ...

What is the best way to prioritize the display of custom login buttons based on the last button used for login?

I have implemented 4 different login methods for my app, each with its own associated component. I am looking to rearrange the buttons based on the last used login method. I already have a function to determine the last login method. let lastSignedInMetho ...

Return a potential undefined output

I am working with a variable called root which could potentially be undefined. Its value is only determined at runtime. const root = resolvedRoot || await this.fileSystem.getCurrentUserHome(); console.log('root.uri = ' + root.uri); The existenc ...

Bringing TypeScript modules from a local module into a React application

As I work on organizing my projects and keeping logic separate in components that will eventually be published, I have a specific structure set up for now: I have a library of Typescript scripts within a project named project-a A separate React app create ...

Utilizing a switch case for typing

I am working on a React component that takes in a list and a type as props. The list is an array of objects, while the type is an optional enum string. Inside this component, there is a function that uses a switch case statement to enforce a specific type ...

The base URL specified in the tsconfig file is causing the absolute path to malfunction

Displayed below is the layout of my folders on the left, along with a metro error in the terminal and my tsconfig.json configuration with baseUrl set to './src'. Additionally, I have included screenshots of my app.ts and MainTabs.ts for further c ...

How Typescript allows variables to act as references to other variables

Imagine having a component with aliases for user data. This approach allows for shorter and cleaner variable names, making the code easier to read: @Component({ selector: 'my-view', templateUrl: './view.component.html', sty ...