Website-Suche

javascript - Typescript bedingtes Erweitern basierend auf dem Wert von Parametern


Ich bin ziemlich neu in TypeScript und ich versuche, ein bedingtes Extends basierend auf dem Wert eines Funktionsarguments zu haben. Der Kontext ist, ein Widget für die Objektauswahl zu definieren, das entweder ein Select, eine Liste von Radios (eine Radio-Gruppe) oder eine Liste von Checkboxen (eine Checkbox-Gruppe) sein kann. Jedes dieser verschiedenen Widgets akzeptiert unterschiedliche Props: SelectProps, RadioGroupProps und CheckboxGroupProps. Die Widget-Auswahl basiert auf zwei Parametern: expand (bedeutet, dass wir eine Liste von Checkboxen oder Radios wollen) und multiple (bedeutet, dass wir mehrere Elemente auswählen wollen oder nicht).

function SelectionWidget(props: SelectionWidgetProps) {}

Hier möchte ich, dass:
wenn props {expand: true, multiple: false} entspricht, dann muss SelectionWidgetProps RadioGroupProps erweitern.
wenn props {expand: true, multiple: true} entspricht, dann muss SelectionWidgetProps CheckboxGroupProps erweitern.
andernfalls muss SelectionWidgetProps SelectProps erweitern.

Ich habe Folgendes versucht:

type SelectTypeExtend<T> = 
    T extends { expand: false } ? SelectProps : 
    T extends { expand: true; multiple: false } ? RadioGroupProps :
    T extends { expand: true; multiple: true; } ? CheckboxGroupProps :
    never;

interface SelectTypeProps extends SelectTypeExtends {
    multiple?: boolean;
    expand?: boolean;
}

Aber es funktioniert nicht, da SelectTypeExtends ein Argument erwartet. Ist es möglich, dies mit TypeScript zu erreichen? Danke!

Antwort #1

Sie können interface nur mit statisch bekannten Typen erweitern. Das bedeutet, dass Sie SelectTypeProps nicht mit SelectTypeExtends erweitern können, weil SelectTypeExtends eine Blackbox ist. Niemand kennt den Typ von T.

Um diesen Fall zu verwalten, sollten Sie type anstelle von interface verwenden:

type SelectProps = {
  tag: 'SelectProps'
}

type RadioGroupProps = {
  tag: 'RadioGroupProps'
}

type CheckboxGroupProps = {
  tag: 'CheckboxGroupProps '
}

type SelectTypeExtend<T> =
  T extends { expand: false } ? SelectProps :
  T extends { expand: true; multiple: false } ? RadioGroupProps :
  T extends { expand: true; multiple: true; } ? CheckboxGroupProps :
  never;

type SelectTypeProps<T> = { // <------ type instead of interface
  multiple?: boolean;
  expand?: boolean;
} & SelectTypeExtend<T>

Beispiel mit React-Komponenten:

import React from "react";

import {
  Select,
  SelectProps,
  Checkbox,
  CheckboxGroupProps,
  Radio,
  RadioGroupProps
} from "formik-antd";


export type SelectionComponentProps =
  | { expand?: false } & { options: SelectProps }
  | { expand: true; multiple?: false } & { options: RadioGroupProps }
  | { expand: true; multiple: true } & { options: CheckboxGroupProps };

function SelectionComponent(props: SelectionComponentProps) {
  if (props.expand) {
    if (props.multiple) {
      return <Checkbox.Group {...props.options} />;
    } else {
      return <Radio.Group {...props.options} />;
    }
  }
  return <Select {...props.options} />;
};

const select: SelectProps = 'unimplemented' as any;
const radio: RadioGroupProps = 'unimplemented' as any;
const checkbox: CheckboxGroupProps = 'unimplemented' as any;

export const TestRadio = () => {
  // Accept properties from RadioGroupProps but not from SelectProps or CheckboxGroupProps
  return [
    <SelectionComponent expand options={radio} />, // ok
    <SelectionComponent expand options={select} />, // expected error
    <SelectionComponent expand options={checkbox} />, // expected error
  ]
};

export const TestCheckbox = () => {
  // Accept properties from CheckboxProps but not from SelectProps or RadioGroupProps
  return [
    <SelectionComponent expand multiple options={checkbox} />, // ok
    <SelectionComponent expand multiple options={select} />, // expected error
    <SelectionComponent expand multiple options={radio} /> // expected error
  ]
};

export const TestSelect = () => {
  // Accept properties from SelectProps but not from RadioGroupProps or CheckboxGroupProps
  return [
    <SelectionComponent options={select} />, // ok
    <SelectionComponent options={radio} />, // expected error
    <SelectionComponent options={checkbox} />, // expected error
  ]
};

Spielplatz

Ich habe den Typ SelectionComponentProps leicht geändert. Bitte beachten Sie, dass TypeScript, wenn Sie einer React-Komponente eine Eigenschaft nicht bereitstellen, diese als undefined und nicht als false behandelt, daher müssen einige Ihrer false-Eigenschaften als partial deklariert werden.