Utilizing Pick to define a type that is a combination of multiple types

I am currently working on simplifying an existing interface by creating a new type. The original type is derived from @mui/x-data-grid and is as follows:

export declare type GridEnrichedColDef<R extends GridValidRowModel = any, V = any, F = V> = GridColDef<R, V, F> | GridActionsColDef<R, V, F>;

The properties that I want to include in the new type are:

export type SupportedColumnProps =
  | 'field'
  | 'headerName'
  | 'width'
  | 'renderCell'
  | 'valueFormatter'
  | 'flex'
  | 'sortable'
  | 'hide'
  | 'type'
  | 'cellClassName';

In addition, I want to support the getActions property if the type property is set to 'actions'. However, I am facing an issue in defining the type to recognize that it should allow getActions only when the type is set to 'actions'.

How can I simplify the interface and provide default values for this new type?

Thank you!

Update 1/30/2023

Based on @Oblosys's response, I have updated the type definition as follows:

export type SupportedColumnProps =
  | 'field'
  | 'headerName'
  | 'width'
  | 'renderCell'
  | 'renderHeader'
  | 'valueFormatter'
  | 'flex'
  | 'sortable'
  | 'hide'
  | 'type'
  | 'cellClassName'
  | 'editable'
  | 'getActions'
  | 'valueOptions';

// Reference: https://stackoverflow.com/questions/75271774/creating-a-type-using-pick-with-a-type-that-is-defined-as-a-union-of-types/75290368#75290368
type KeyOfUnion<T> = T extends unknown ? keyof T : never;
type PickUnion<T, K extends KeyOfUnion<T>> = T extends unknown
  ? K & keyof T extends never
    ? never
    : Pick<T, K>
  : never;
export type GridColumn = PickUnion<GridEnrichedColDef, SupportedColumnProps> & { enableColumnMenu?: boolean };

However, I am now encountering an issue where if I define a column without the type or getActions properties, it does not satisfy the defined type. For example:

const column: GridColumn = {
  field: 'hello'
}

https://i.sstatic.net/Ngdwg.png

Answer №1

When dealing with a union and applying the Pick method, there are some nuances to consider. One challenge arises from the K extends keyof T constraint of Pick, where keyof only returns keys that are common in all union members. This means you cannot select a property that exists in only some of the union members. Additionally, applying Pick to a union results in a single object with union property values rather than a union of objects. For example,

Pick<{a: number} | {a: string}, 'a'>
gives {a: number | string} instead of {a: number} | {a: string}.

To address this issue, you can use a dummy extends clause to create a distributive conditional type that applies keyof to each union member and merges the results:

type KeyOfUnion<T> = T extends unknown ? keyof T : never

type Test = KeyOfUnion<{a: number, b: boolean} | {b: string, c: symbol}>
// type Test = "a" | "b" | "c"

By incorporating KeyOfUnion in the constraint, you can utilize another distributive conditional type to employ Pick on each union member while using an intersection to select only relevant keys:

type PickUnionNaive<T, K extends KeyOfUnion<T>> =
  T extends unknown ? Pick<T, K & keyof T> : never

type Test = PickUnionNaive<{a: number, b: boolean} | {a: string}, 'a'>
// type Test = PickUnionNaive<{a: number, b: boolean}, "a"> | PickUnion<{a: string}, "a">
// resulting in: {a: number} | {a: string}

Nevertheless, this approach encounters issues with keys that are not shared among all union members, leading to empty {} types in the union when using never as the key parameter:

type Test = PickUnionNaive<{a: number, b: boolean} | {a: string}, 'b'>
// type Test = Pick<{a: number, b: boolean}, "b"> | Pick<{a: string}, never>
// resulting in: {b: boolean} | {}

This situation essentially turns the resulting union into {}, allowing any type to be assigned. To eliminate the empty objects, an additional conditional statement can be included to return never if the intersection between the keys to pick and the keys of the union member is empty. This leads to the revised definition of PickUnion:

type PickUnion<T, K extends KeyOfUnion<T>> =
  T extends unknown
  ? K & keyof T extends never ? never : Pick<T, K & keyof T>
  : never

type Test = PickUnion<{a: number, b: boolean} | {a: string}, 'b'>
// type Test = {b: boolean}

Explore this example on TypeScript 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

issue with visibility of checkbox in material ui table row

In a separate file called TradesTable.js, I have created a table using Material UI for my React app. const DummyTableRow = (props) => { let myRows = props.trades.map((trade, index) => { return <TableRow> <TableRowColumn ...

Tips for displaying only the initial 15 characters of a text value

The content extracted from a .ts file is being displayed on the home.html page. I am aiming to display only the initial 15 characters followed by 3 dots (...). Despite my efforts, the following code snippet is not functioning as expected: home.html < ...

Enhancing React MaterialUI Theming: Boosting Transition Duration for Theme Switching

I'm currently working on a light/dark theme using React MaterialUi. Curious about: Is there a way to make the theme transition smoother instead of an abrupt change? The Issue: I attempted using inline styling with style={{transition: "all 1s ...

Angular: Initiate multiple functions simultaneously and combine results afterwards

My current code successfully zips and saves the response of a JSON array by splitting them into individual files using a single method. zip: JSZip = new JSZip(); folder: JSZip = new JSZip(); this.apicall.api1() .subscribe( response => { for (let r ...

Please refrain from utilizing MUI - useGridRootProps outside of the DataGrid/DataGridPro component

When we click on "Filter" in the context menu of our DataGrid, we encounter this error. We are working on implementing a component hierarchy for the DataGrid as follows: MigrationJobTable.tsx: return ( <div data-testid='migrationJobTa ...

Is it possible to assign a string literal type as a key in TypeScript mappings?

Is it possible to map a string literal type as a key in a new type? I attempted this, but it did not work as expected. const obj = { type: "key", actions: { a() {}, b() {}, }, } as const; /* Map to { key: { a() {}, b() {}, } */ t ...

Arrange photos in a grid using React Dropzone uploaders

I'm having trouble figuring out how to create a custom preview similar to an image set in a grid using the react-dropzone-uploader component for uploading multiple files. Currently, when I upload images, they are displayed as shown in my normal uploa ...

What is the process for sending JSON data in a file to a django rest api?

Currently, I am using Angular on the front-end and Django Rest on the back-end. I have encountered a scenario where I need to create a complex model. Despite considering other simpler solutions, I believe that using JSON to pass the files can streamline th ...

Unfulfilled expectation of a promise within an array slipping through the cracks of a for loop

I have a function that generates a Promise. Afterward, I have another function that constructs an array of these promises for future utilization. It is important to note that I do not want to execute the promises within the array building function since so ...

ngOnInit unable to properly listen to event stream

I've been trying to solve a persistent issue without success. The problem involves the interaction between three key elements: the HeaderComponent, TabChangingService, and TabsComponent. Within the HeaderComponent, there are three buttons, each with a ...

Having trouble pushing data to a GraphQL database from my Next.js application

I am currently working on developing a Reddit-like platform. To simplify the process, I have integrated SQL with graphQL using Stepzen for endpoints. Below is the code snippet of my Post Box component for the site, which includes graphQL mutations.ts and q ...

Having an issue with TypeScript and React where the onChange event on the <select> element is only setting the previous value instead of the current value when using the useState hook

I'm currently developing a scheduling web tool. One of the key features I'm working on involves calculating the total hours between two selected times, startTime and endTime. These times are chosen via a form and stored using the useState hook: ...

Deep Dive into TypeScript String Literal Types

Trying to find a solution for implementing TSDocs with a string literal type declaration in TypeScript. For instance: type InputType = /** Comment should also appear for num1 */ 'num1' | /** Would like the TSDoc to be visible for num2 as well ...

What is the proper way to define a new property for an object within an npm package?

Snippet: import * as request from 'superagent'; request .get('https://***.execute-api.eu-west-1.amazonaws.com/dev/') .proxy(this.options.proxy) Error in TypeScript: Property 'proxy' is not found on type 'Super ...

The Art of Typing in TypeScript Classes

I am working with an interface or abstract class in TypeScript, and I have numerous classes that implement or extend this interface/class. My goal is to create an array containing the constructors of all these subclasses, while still ensuring that the arra ...

Incorporate a tooltip feature for enhancing user experience with filtering on Mui

I need to customize the tooltip for the filter icon in a mui datatable within my project. The current tooltip displays as 'Filter Table' and I would like to change it to just 'Filter': Filter Image I attempted to make this adjustment b ...

Which is better for a React app theme: using material-ui makeStyles or SCSS architecture?

Recently, I became a part of a group working on a React application. Our current approach involves using Material UI and its makeStyles function for styling the app. One of my upcoming responsibilities is to determine the global theme for our project. Pers ...

How to implement automatic scrolling to the bottom of a div in React

Currently facing an issue in React: I am looking to implement auto-scroll functionality when the page loads, so it scrolls to the bottom of the messages box. Here is my current code snippet: import Title from "components/seo/Title"; import { u ...

Top method for changing Enum to Set in TypeScript

Take a look at the enum below: enum Animes { OnePiece = 'One Piece', Naruto = 'Naruto', Bleach = 'Bleach' } How can we efficiently transform this enum into a Set? ...

Uncover the solution to eliminating webpack warnings associated with incorporating the winston logger by utilizing the ContextReplacementPlugin

When running webpack on a project that includes the winston package, several warnings are generated. This is because webpack automatically includes non-javascript files due to a lazy-loading mechanism in a dependency called logform. The issue arises when ...