Conditional props in intreface - reactjs

I have a question regarding interfaces:
Basically I'm creating a dynamic component that change depending on which page it is. My interface is constructed like so:
interface Props {
heading: string;
description: string;
signUp?: boolean;
signUpButton1?: string;
signUpButtonLink1?: string;
signUpButton2?: string;
signUpButtonLink2?: string;
startFreeAccount?: boolean;
startFreeAccountSignUpButton?: string;
startFreeAccountLink1?: string;
startFreeAccountSignUpLink1?: string;
startFreeAccountSignUpLink2?: string;
startFreeAccountSignUpLink3?: string;
contactUsForDemo?: boolean;
contactUsForDemoInput1?: string;
contactUsForDemoInput2?: string;
contactUsForDemoInput3?: string;
contactUsForDemoButtonText?: string;
ThanksForDemo?: boolean;
ThanksForDemoButton?: string;
}
In my code I then use for example:
{props.signUp &&
<div className="mt-10 flex flex-col items-center gap-x-6">
<Link
href={"/signUpPractitioner/startFreeAccount"}
className=" bg-black mb-4 -ml-[14vw] px-16 py-1.5 text-base font-semibold leading-7 text-white shadow-sm outline outline-offset-0 outline-black"
>
{props.signUpButton1}
</Link>
<Link href={"/practitioner/contactUsForDemo"}
className=" bg-white -ml-[14vw] px-10 py-1.5 text-base font-semibold leading-7 text-black outline outline-offset-0 outline-black shadow-sm"
>
{props.signUpButton2} <span aria-hidden="true">→</span>
</Link>
</div> }
Setting "signUp == true" would display the information in the above part of the component. My issue is that Link (from NEXTJS) cannot handle the fact that "signUpButtonLink1" might be undefined. If I put that "signUpButtonLink1" has to have a value, then even if I don't use the "signUp" part of the component, I still need to assign some value to "signUpButtonLink1" which obviously doesnt make sense (it makes sense in terms of how the current interface is constructed but not in terms of functionality).
My question is: Is it possible to set that "signUpButton1, signUpLink1 etc etc" only needs to be set if "signUp === true" ? It the same for all bools in the interface and the props "affiliated" with it.
Thanks for your help!

You can do this with simple unions
type Props = {
heading: string;
description: string;
} & (
| {
signUp?: false
signUpButton1?: never
// ...
}
| {
signUp: true
signUpButton1: string
// ...
}
) & (
| {
startFreeAccount?: false;
startFreeAccountSignUpButton?: never;
// ...
}
| {
startFreeAccount: true;
startFreeAccountSignUpButton: string;
// ...
}
) & (
...
)
Maybe add a utility type:
type OptionalProps<K extends string, T> =
| { [key in K]?: false } & { [key in keyof T]?: never }
| { [key in K]: true } & T
type Props = {
heading: string
description: string
}
& OptionalProps<'signUp', {
signUpButton1: string,
}>
& OptionalProps<'startFreeAccount', {
startFreeAccountSignUpButton: string
}>
& ...
The properties with ?: never are not necessary, but they will make your life easier if you destructure props into individual variables. Without ?: never you won't be able to do it, it will tell that property with name ... doesn't exist in the object.
Or you can just use objects instead of inline props:
type Props = {
heading: string
description: string
signUp?: {
button1: string
button2: string
// ...
}
startFreeAccount?: {
signUpButton: string
// ...
}
// ...
}
I would even argue that these objects is easier to read and use

Related

type '{ mail: any; }' is missing the following properties from type '{ mail: string; name: boolean; } name

Two parent components are sharing the same child components, but have differen properties passed.
Since ParentB doesn't pass the name props, it throws the error
type '{ mail: any; }' is missing the following properties from type '{ mail: string; name: string; } name
See the example:
const ParentA=()=>{
return (
<>
<Child
mail={project.mail}
name={name}
/>
</>
)
}
const ParentB=()=>{
return (
<>
<Child
mail={project.mail}
/>
</>
)
}
const Child: FunctionComponent<{
mail: string;
name: string;
}> = ({ mail, name }) => {
}
I tried to solve it this way in Child component, but it throws another error in jsx
interface ChildProps {
mail: string;
name: boolean;
}
interface CHildWitouthNameProps {
mail: string;
name?: never;
}
type Props = ChildProps | CHildWitouthNameProps;
const Child = (props: Props) => {
Another try was
const Child: FunctionComponent<{
mail: string;
name?: boolean;
}> = ({ mail, name }) => {
}
But it throws another error in jsx
name is possibly 'undefined'
return (
<div>
{name}
</div>
)
How to fix the error?
Any Help will be appreciated.
Your last approach
const Child: FunctionComponent<{
mail: string;
name?: boolean;
}> = ({ mail, name }) => {
}
seems correct, since logically the child component must have a mail prop, but it doesn't necessarily need to have a name prop. Based on this definition, the second error you describe,
name is possibly 'undefined'
seems like TypeScript functioning as intended. It's telling you that there is a prop which might not be defined, being used in a way that would break the application if it actually was undefined. The way to fix this is by ensuring that wherever you use the name prop in a way that's sensitive to whether or not it's defined, you should include good checks. There are a couple ways to do this:
return (
<div>
{name || "No name given"}
</div>
)
^this way fills in the name with the placeholder whenever it's not present,
return (
{name && <div>
{name}
</div>}
)
^this way only renders the whole div if name is defined to begin with.
Again, it seems like it depends on what you're going for, but this might be some good things to try first.
Just add ? which means that that is an optional key.
const ParentA=()=>{
return (
<>
<Child
mail={project.mail}
loading={loading}
/>
</>
)
}
const ParentB=()=>{
return (
<>
<Child
mail={project.mail}
/>
</>
)
}
const Child: FunctionComponent<{
mail: string;
loading?: boolean;
}> = ({ mail, loadedData }) => {
}

Interface for a React Button component that can be an anchor tag or a button tag

I'm trying to create a button component that can be an anchor tag with a href or a button tag with a type prop. I have the following code:
interface IBaseProps {
children: string;
fullSize?: boolean;
theme?: 'primary' | 'secondary' | 'dark';
}
interface ILinkButtonProps extends IBaseProps {
url: string;
type: never;
props?: AnchorHTMLAttributes<HTMLAnchorElement>;
}
interface IButtonProps extends IBaseProps {
type: 'button' | 'submit' | 'reset';
url: never;
props?: ButtonHTMLAttributes<HTMLButtonElement>;
}
export const Button = ({
children,
props,
theme = 'primary',
fullSize = false,
type = 'button',
url,
}: IButtonProps | ILinkButtonProps): JSX.Element => {
const Tag: keyof JSX.IntrinsicElements = url ? 'button' : 'a';
return (
<Tag
className={`${styles.button} ${styles[theme]} ${
fullSize ? styles.fullSize : ''
}`} // not important
{...props}
{...(Tag === "button" ? {type: `${type}`} : {href: url})}
>
{children}
</Tag>
);
};
However, that gives me some typing errors, for instance:
Types of property 'onCopy' are incompatible.
Type 'ClipboardEventHandler | undefined' is not assignable to type 'ClipboardEventHandler | undefined'.
Type 'ClipboardEventHandler' is not assignable to type 'ClipboardEventHandler'.
Type 'HTMLAnchorElement' is missing the following properties from type 'HTMLButtonElement': disabled, form, formAction, formEnctype, and 11 more.
Is there a way for me to format my code with typescript, so I can achieve a component that allows me to have both a button or a link?
Having the separate Tag variable is your downfall. Typescript is not intelligent enough to "see through" this variable and use it as a union discriminator. Once you assign to to a diff variable, the context of its meaning is lost.
You have to be quite explicit and pass the whole props into your new type guard, then consume that immediately after without assigning flags that you use later.
You will need strict null checks on for this to work. Heres a code sandbox https://codesandbox.io/s/heuristic-haslett-ovqkcw
import React from "react";
type IBaseProps = {
children: string;
fullSize?: boolean;
theme?: "primary" | "secondary" | "dark";
};
type ILinkButtonProps = IBaseProps & {
url: string;
type?: never;
props?: React.AnchorHTMLAttributes<HTMLAnchorElement>;
};
type IButtonProps = IBaseProps & {
type: "button" | "submit" | "reset";
url?: never;
props?: React.ButtonHTMLAttributes<HTMLButtonElement>;
};
export const Button = (props: ILinkButtonProps | IButtonProps): JSX.Element => {
const { children, theme, fullSize, type, url } = props;
const commonProps = {
className: `${styles.button} ${styles[theme]} ${
fullSize ? styles.fullSize : ""
}`
};
if (props.type) {
return (
<button {...commonProps} type={type} {...props.props}>
{children}
</button>
);
}
return (
<a {...commonProps} href={url} {...props.props}>
{children}
</a>
);
};
BTW, in my opinion, you shouldn't do what you are trying to do. Whilst its possible, this component likely breaks the principle of element of least surprise for the developer. Links and buttons are not semantically the same -- the dev should really make a very active choice.

Typescript and interface arrays undefined and not assignable

I'm having trouble getting the syntax right for the Typescript React Props.
I have an array of people each of which may have 0 - many cars. I have a people container that will contain all people, each person will have a car container that may contain cars, and car components that will be inside the car container.
I need to have the container for cars anyways because I will add edit buttons which will add cars to the person.
I'll add my code from top down:
PersonSlice:
export interface PersonState {
id?: number;
firstname?: string;
lastname?: string;
email?: string;
created_at?: any;
updated_at?: any;
cars?: [];
}
People.tsx:
function People() {
const people = useAppSelector(selectPerson); //person is of type interface PersonState[]
let contents;
contents = <><div className="personcontainer">
{people && people.length > 0 && people.map(person => {
return <div key={person.id}>
<Person
dispatch={dispatch}
person={person}
toggleEditForm={() => toggleEditForm(person.id)}
personToEdit={personToEdit}
submitEdit={submitEdit}
/>
</div>
})}
</div></>
});
}
This is where I start to have problems -
Person.tsx:
interface PersonProps {
dispatch: Dispatch<any>;
person: PersonState;
toggleEditForm: () => void;
submitEdit: any;
personToEdit: number;
}
function Person(props: PersonProps) {
return (
<div className="person">
<Cars cars={props.person.cars}/> //cars error: type [] | undefined not assignable to car[]
</div>
);
}
cars.tsx:
import {car, Car} from './car';
interface cars {
cars: car[];
}
function Cars (props: cars) {
return (
<div className="carcontainer">
<h2>cars container</h2>
{props.cars && props.cars.map((carobj: car) => {
<Car car={carobj} key={}/> //car error: Type '{ car: car; key: any; }' is not assignable to type 'IntrinsicAttributes & car'.
})}
</div>
)
}
export default Cars;
and finally car.tsx:
export interface car {
year: number,
make:string,
model: string,
price: number,
person_id: number,
}
export function Car (props: car) {
return (
<div className="carcontainer">
<h3>
{props.year} {props.make} {props.model} {props.price}
</h3>
</div>
)
}
So I have two errors, one in person.tsx and one in cars.tsx which I added as comments in the code.
I've read like a dozen questions on this but I'm still super confused. What am I doing wrong?
Thanks
The fix to both issues is in cars.tsx.
import {car, Car} from './car';
interface cars {
cars?: car[]; // make this optional since the data from PeopleState is optional
}
function Cars (props: cars) {
return (
<div className="carcontainer">
<h2>cars container</h2>
{props.cars && props.cars.map((carobj: car) => {
<Car {...carobj} key={}/> // spread carobj into the component
})}
</div>
)
}
export default Cars;
<Cars cars={props.person.cars}/> //cars error: type [] | undefined not assignable to car[]
This isn’t really an error. You specifically wrote:
cars?: [];
This means, “I might not know where or not the person has any cars.” If you don’t, obviously you cannot render the list of cars.
Maybe you really might not know, in which case, your code should be something like:
{ props.person.cars?
<Cars cars={props.person.cars}/>
: <span>I do not know if he has cars</span> }
Or, you really know about the cars, but you might know that there are no cars, in which case you should say so:
cars: [];
In the second issue, you need to actually give it a key:
{props.cars && props.cars.map((carobj: car, idx) =>
<Car car={carobj} key={idx}/> )}

Difference between switch function and React(jsx,tsx) switch operator

We are currently porting our portfolio from Javascript to TypeScript using NextJS as frontend framework and Strapi as backend.
To have dynamic content we created a dynamiczone field inside of the post model and we get it from GraphQL.
Our problem comes when we want to render the content based on the dynamic zone type, its model is:
export interface IExperience {
id: string;
from: Date;
to?: Date;
ongoing?: boolean;
title: string;
institution: string;
address?: IAddress;
url?: string;
description?: string;
}
export interface IPersonalInformation {
id: string;
name: string;
photo: IFile;
position: string;
nationality?: string;
address?: IAddress;
telephone?: ITelephone[];
mail: string;
links?: ISocialLink[];
aboutMe?: string;
}
export interface IRichText {
id: string;
text: string;
}
export type IComponent =
| ({
__component: "content.rich-text";
__typename: "ComponentContentRichText";
} & IRichText)
| ({
__component: "content.experience";
__typename: "ComponentContentExperience";
} & IExperience)
| ({
__component: "content.personal-information";
__typename: "ComponentContentPersonalInformation";
} & IPersonalInformation)
| ({
__component: "fields.skill";
__typename: "ComponentFieldsSkill";
} & ISkill);
The component field will extends one interface based in its type; cool, but when we go to render it we get problems:
const DynamicZone: React.FC<IDynamicZone> = ({ component, className }) => {
const classes = useStyles();
const selectComponent = () => {
switch (component.__typename) {
case "ComponentContentRichText":
return <Content>{component.text}</Content>;
case "ComponentContentExperience":
return <Experience {...component} />;
case "ComponentContentPersonalInformation":
return <PersonalInformation {...component} />;
case "ComponentFieldsSkill":
return <Skill {...component} />;
}
};
return (
<Typography
variant="body1"
component="section"
className={clsx(classes.dynamicZone, className)}
>
{
{
"content.rich-text": <Content>{component.text}</Content>, <-- Bug 1
"content.experience": <Experience {...component} />,
"content.personal-information": (
<PersonalInformation {...component} /> <-- Bug 2
),
"fields.skill": <Skill {...component} />,
}[component.__component]
}
</Typography>
);
};
export default DynamicZone;
With, bug 1:
<html>TS2339: Property 'text' does not exist on type 'IComponent'.<br/>Property 'text' does not exist on type '{ __component: "content.experience"; __typename: "ComponentContentExperience"; } & IExperience'.
And bug 2:
<html>TS2322: Type '{ __component: "content.rich-text"; __typename: "ComponentContentRichText"; id: string; text: string; } | { __component: "content.experience"; __typename: "ComponentContentExperience"; ... 8 more ...; description?: string | undefined; } | { ...; } | { ...; }' is not assignable to type 'IntrinsicAttributes & IPersonalInformation & { children?: ReactNode; }'.<br/>Type '{ __component: "content.rich-text"; __typename: "ComponentContentRichText"; id: string; text: string; }' is missing the following properties from type 'IPersonalInformation': name, photo, position, mail
Why is it assing the type improperly?
Ok, if we change it to selectComponent function, it does not give any error:
const DynamicZone: React.FC<IDynamicZone> = ({ component, className }) => {
const classes = useStyles();
const selectComponent = () => {
switch (component.__typename) {
case "ComponentContentRichText":
return <Content>{component.text}</Content>;
case "ComponentContentExperience":
return <Experience {...component} />;
case "ComponentContentPersonalInformation":
return <PersonalInformation {...component} />;
case "ComponentFieldsSkill":
return <Skill {...component} />;
}
};
return (
<Typography
variant="body1"
component="section"
className={clsx(classes.dynamicZone, className)}
>
{selectComponent()}
</Typography>
);
};
Esentially, it is the same thing, so, why it does not give typing errors with switch case but it does with {{}[]}?
Thanks.
You have provided a type guard in selectComponent() by switching between the values of component.__typename, which narrows the type that component can be. Since the case of "ComponentContentRichText" can only narrow down to a single type in the IComponent union,
{ __component: "content.rich-text"; __typename: "ComponentContentRichText"; } & IRichText,
it is known that the text property exists on component via the IRichText interface.
In the example of dynamic selection with an object and index ({...}[...]), you have not narrowed the type of component. The object is created with all of the members - regardless of the component.__component value - and then the value is selected dynamically with an index of the component.__component value. The transpiler cannot tell that text is a valid property of component at the time that the object is created.
You could add type guards in the dynamic selection's object instantiation using conditional statements. However, this method is not optimal because there are additional run-time checks to be made.
<Typography>
{
{
'content.rich-text': component.__component === 'content.rich-text'
? <Content>{ component.text }</Content>
: undefined,
/* ... */
}[component.__component]
}
</Typography>
Or, you could use chained conditional statements to select the result.
<Typography>
{
component.__component === 'content.rich-text'
? <Content>{ component.text }</Content>
: component.__component === 'content.experience'
? <Experience { ...component } />
: /* ... */
}
</Typography>
An alternative to the selectComponent() anonymous function to keep the definition inline with the JSX, would be to define an anonymous function and call it immediately. Using a function - whether named, anonymous and defined earlier, or anonymous and called immediately - has the added benefits of being cleaner, easier to reason about, and being able to use switch statement optimizations such as jump tables.
<Typography>
{
function() {
switch (component.__component) {
case 'content.rich-text':
return <Content>{ component.text }</Content>
/* ... */
default:
return null;
}
}()
}
</Typography>

Force a react component to be called with one of two props [duplicate]

I'm trying to create an interface that could have
export interface MenuItem {
title: string;
component?: any;
click?: any;
icon: string;
}
Is there a way to require component or click to be set
Is there a way to require that both properties can't be set?
With the help of the Exclude type which was added in TypeScript 2.8, a generalizable way to require at least one of a set of properties is provided is:
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
}[Keys]
And a partial but not absolute way to require that one and only one is provided is:
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]
Here is a TypeScript playground link showing both in action.
The caveat with RequireOnlyOne is that TypeScript doesn't always know at compile time every property that will exist at runtime. So obviously RequireOnlyOne can't do anything to prevent extra properties it doesn't know about. I provided an example of how RequireOnlyOne can miss things at the end of the playground link.
A quick overview of how it works using the following example:
interface MenuItem {
title: string;
component?: number;
click?: number;
icon: string;
}
type ClickOrComponent = RequireAtLeastOne<MenuItem, 'click' | 'component'>
Pick<T, Exclude<keyof T, Keys>> from RequireAtLeastOne becomes { title: string, icon: string}, which are the unchanged properties of the keys not included in 'click' | 'component'
{ [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>> }[Keys] from RequireAtLeastOne becomes
{
component: Required<{ component?: number }> & { click?: number },
click: Required<{ click?: number }> & { component?: number }
}[Keys]
Which becomes
{
component: { component: number, click?: number },
click: { click: number, component?: number }
}['component' | 'click']
Which finally becomes
{component: number, click?: number} | {click: number, component?: number}
The intersection of steps 1 and 2 above
{ title: string, icon: string}
&
({component: number, click?: number} | {click: number, component?: number})
simplifies to
{ title: string, icon: string, component: number, click?: number}
| { title: string, icon: string, click: number, component?: number}
Not with a single interface, since types have no conditional logic and can't depend on each other, but you can by splitting the interfaces:
export interface BaseMenuItem {
title: string;
icon: string;
}
export interface ComponentMenuItem extends BaseMenuItem {
component: any;
}
export interface ClickMenuItem extends BaseMenuItem {
click: any;
}
export type MenuItem = ComponentMenuItem | ClickMenuItem;
There is a simpler solution. No need to rely on any or
complex conditional types (see answer):
Is there a way to require component or click to be set? (Inclusive OR)
type MenuItemOr = {
title: string;
icon: string;
} & ({ component: object } | { click: boolean })
// brackets are important here: "&" has precedence over "|"
let testOr: MenuItemOr;
testOr = { title: "t", icon: "i" } // error, none are set
testOr = { title: "t", icon: "i", component: {} } // ✔
testOr = { title: "t", icon: "i", click: true } // ✔
testOr = { title: "t", icon: "i", click: true, component: {} } // ✔
A union type (|) corresponds to inclusive OR. It is intersected with the non-conditional properties.
Use the in operator to narrow the value back to one of the constituents:
if ("click" in testOr) testOr.click // works
Is there a way to require that both properties can't be set? (Exclusive OR / XOR)
type MenuItemXor = {
title: string;
icon: string;
} & (
| { component: object; click?: never }
| { component?: never; click: boolean }
)
let testXor: MenuItemXor;
testXor = { title: "t", icon: "i" } // error, none are set
testXor = { title: "t", icon: "i", component: {} } // ✔
testXor = { title: "t", icon: "i", click: true } // ✔
testXor = { title: "t", icon: "i", click: true, component: {} } //error,both set
Basically either component or click can be set, the other should never 1 be added at the same time. TS can make a discriminated union type out of MenuItemXor, which corresponds to XOR.
This XOR condition for MenuItemXor is not possible with accepted answer.
Playground
1 Technically, prop?: never gets resolved to prop?: undefined, though former is often used for illustration.
An alternative without multiple interfaces is
export type MenuItem = {
title: string;
component: any;
icon: string;
} | {
title: string;
click: any;
icon: string;
};
const item: MenuItem[] = [
{ title: "", icon: "", component: {} },
{ title: "", icon: "", click: "" },
// Shouldn't this error out because it's passing a property that is not defined
{ title: "", icon: "", click: "", component: {} },
// Does error out :)
{ title: "", icon: "" }
];
I've asked a similar question at How to create a Partial-like that requires a single property to be set
The above could be simplified, but it may or may not be easier to read
export type MenuItem = {
title: string;
icon: string;
} & (
{component: any} | {click: string}
)
Note that none of these prevent you from adding both because TypeScript does allow extra properties on objects that use AND/OR See https://github.com/Microsoft/TypeScript/issues/15447
I use this:
type RequireField<T, K extends keyof T> = T & Required<Pick<T, K>>
Usage:
let a : RequireField<TypeA, "fieldA" | "fieldB">;
This makes fieldA and fieldB required.
I ended up doing:
export interface MenuItem {
title: string;
icon: string;
}
export interface MenuItemComponent extends MenuItem{
component: any;
}
export interface MenuItemClick extends MenuItem{
click: any;
}
Then I used:
appMenuItems: Array<MenuItemComponent|MenuItemClick>;
But was hoping there was a way to model it with a single interface.
I like using Pick along with a base type that includes all properties to establish these kinds of conditional requirements.
interface MenuItemProps {
title: string;
component: any;
click: any;
icon: string;
}
export interface MenuItem =
Pick<MenuItemProps, "title" | "icon" | "component"> |
Pick<MenuItemProps, "title" | "icon" | "click">
This is clean and also flexible. You can get arbitrarily complex with your requirements, asserting things like "require either all the properties, just these two properties, or just this one property" and so on while keeping your declaration simple and readable.
Here's a simple way to implement either but not both
type MenuItem = {
title: string;
component: any;
click?: never;
icon: string;
} | {
title: string;
component?: never;
click: any;
icon: string;
}
// good
const menuItemWithComponent: MenuItem = {
title: 'title',
component: "my component",
icon: "icon"
}
// good
const menuItemWithClick: MenuItem = {
title: 'title',
click: "my click",
icon: "icon"
}
// compile error
const menuItemWithBoth: MenuItem = {
title: 'title',
click: "my click",
component: "my click",
icon: "icon"
}
Yet another solution:
type RequiredKeys<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>;
type MenuItem2 = RequiredKeys<MenuItem, "component" | "click">;
This approach combines never and Omit. Benefits here are that it's easy to understand and also easy to update if you need to add more properties.
interface Base {
title: string;
icon: string;
component?: never;
click?: never;
}
interface OnlyComponent {
component: any;
}
interface OnlyClick {
click: any;
}
export type MenuItem = (Omit<Base, 'component'> & OnlyComponent) | (Omit<Base, 'click'> & OnlyClick);
You can use in to narrow an instance of MenuItem:
const item: MenuItem = {
title: 'A good title';
icon: 'fa-plus';
component: SomeComponent;
};
//...
if('component' in item) {
const Comp = item.component;
//...
}
To just extends upon the cool answers above! And for the people that land here while searching for a Partial version with requiring capability! Here a snippet i made to take!
PartialReq
You want to have a Partial of an interface, but in mean time require some of the fields! Here how it's done
export type PartialReq<T, Keys extends keyof T = keyof T> =
Pick<Partial<T>, Exclude<keyof T, Keys>>
& {
[K in Keys]: T[K]
};
Use example
export interface CacheObj<SigType = any, ValType = any> {
cache: Map<SigType, ValType>,
insertionCallback: InsertionCallback<SigType, ValType> // I want this to be required
}
// ...
export class OneFlexibleCache<SigType = any, ValType = any> {
private _cacheObj: CacheObj<SigType, ValType>;
constructor(
cacheObj: PartialReq<CacheObj<SigType, ValType>, 'insertionCallback'> // <-- here
// i used it
) {
cacheObj = cacheObj || {};
this._cacheObj = {
// ...
// _______________ usage
this._caches.set(
cacheSignature,
new OneFlexibleCache<InsertionSigType, InsertionValType>({
insertionCallback // required need to be provided
})
);
Here you can see that it work perfectly
If the required not provided
UPDATE: For the usage that i implied above here a better answer
I just went by the doc and found Omit.
https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk
I came to add it. But before i do, I just seen this cool answer. It cover all:
https://stackoverflow.com/a/48216010/7668448
Just check it out! It show how to do it for all the different version of Typescript! And for the sake of not repeating ! Go and check!

Resources