Hyeon__
Hyeon__

Reputation: 69

How to multiple keys in typescript interface?

I am developing in a React + TypeScript environment.

I want to has multiple keys to the 'content' key.

    interface IModalProps {
        isOpen: boolean;
        type: 'basic';
        content: ReactElement;
        options?: IModalOption;
}

I imagined it with the code below.

interface IModalProps {
        isOpen: boolean;
        type: 'basic';
        [content | children]: ReactElement;
        options?: IModalOption;
}

the code above means key would be 'content' or 'children'.

Is there a way to have multiple keys in the type script? thanks.

Upvotes: 1

Views: 1324

Answers (2)

Elias
Elias

Reputation: 4112

Does this do what you want?

interface IModalPropsBase {
    isOpen: boolean;
    type: 'basic';
    options?: IModalOption;
}

type IModalProps = IModalPropsBase
    & ({ content: ReactElement } | { children: ReactElement });

Or mashed into a signle type in case you prefer that:

type IModalProps =
  & { isOpen: boolean; type: 'basic'; options?: IModalOption; }
  & ({ content: ReactElement } | { children: ReactElement });

To then use it, you can utilize the in operator:

function Modal(props: IModalProps) {
    const actualContent = 'children' in props ? props.children : props.content;
    // ...
}

Upvotes: 1

import { ReactElement } from 'react'

type IModalOption = {
    tag: 'IModalOption'
}

interface Props {
    isOpen: boolean;
    type: 'basic';
    options?: IModalOption;
}

type Union<T extends string> = T extends string ? Record<T, ReactElement> : never

type IModalProps = Props & Union<'content' | 'children'>

// error, there are no 'content' and/or 'children'
const props: IModalProps = {
    isOpen: true,
    type: 'basic',
    options: {
        tag: 'IModalOption'
    },
}

// ok
const props2: IModalProps = {
    isOpen: true,
    type: 'basic',
    options: {
        tag: 'IModalOption'
    },
    content: null as any as ReactElement
}

// ok
const props3: IModalProps = {
    isOpen: true,
    type: 'basic',
    options: {
        tag: 'IModalOption'
    },
    children: null as any as ReactElement
}

// ok
const props4: IModalProps = {
    isOpen: true,
    type: 'basic',
    options: {
        tag: 'IModalOption'
    },
    children: null as any as ReactElement,
    content: null as any as ReactElement
}

Union utility type distributes content | children.

It means that it is applied to each union. Union<'content' | 'children'> returns Record<'content, ReactElement> | Record<'children, ReactElement>.

In order to achieve desired behavior, you just need to merge base props with union:

type IModalProps = Props & Union<'content' | 'children'>

Playground

Upvotes: 1

Related Questions