Как изолировать известные свойства на пересечении типа generi c и типа non-generi c - PullRequest
2 голосов
/ 03 мая 2020

У меня есть HO C, который принимает withPaper реквизит, но не передает его компоненту, который будет отображаться.

import React, { ComponentType, FC } from "react";
import { Paper } from "@material-ui/core";

interface WithOptionalPaperProps {
  withPaper?: boolean;
}

export const withOptionalPaper = <Props extends object>() => (
  Component: ComponentType<Props>
) => ({ withPaper, ...otherProps }: Props & WithOptionalPaperProps) => {
  if (withPaper) {
    return (
      <Paper>
        <Component {...otherProps as Props} />
      </Paper>
    );
  }
  return <Component {...otherProps as Props} />;
};

// Code below shows how the code above will be used.

interface NonPaperedComponentProps {
  text: string;
  className: string;
}

const NonPaperedComponent: FC<NonPaperedComponentProps> = props => {
  return <h1 className={props.className}>{props.text}</h1>;
};

// Code will be used like an HOC.
// 'withPaper' prop can be optionally added to wrap the underlying component in 'Paper'
const OptionalPaperedComponent = withOptionalPaper<NonPaperedComponentProps>()(
  NonPaperedComponent
);

// All props except 'withPaper' should be passed to 'NonPaperedComponent'
const renderedComponent = (
  <OptionalPaperedComponent withPaper className="Hello" text="Hello There" />
);

Я удалил ошибки путем приведения типов с помощью otherProps as Props , Без них он выдает ошибку 'Props' could be instantiated with a different subtype of constraint 'object'

https://codesandbox.io/s/gallant-shamir-z2098?file= / src / App.tsx: 399-400

Я бы предположил, что, поскольку я деструктурировал и изолировал известные свойства от Props & WithOptionalPaperProps, типы были бы похожи на это:

{
    withPaper, // type 'WithOptionalPaperProps["withPaper"]'
    ...otherProps // type 'Props'
}

Как сделать так, чтобы Компонент, который withOptionalPaper возвращает с реквизитом withPaper, не передавая его своим потомкам, но все еще пропускаешь все остальные реквизиты?

1 Ответ

4 голосов
/ 03 мая 2020

Это ограничение того, как деструктурированные объекты покоя являются типами. Для длинного типа TS даже не разрешал деструктурирование параметров типа generi c. В версии 3.2 была добавлена ​​возможность использовать переменные отдыха с параметрами типа c ( PR ), но переменная отдыха вводится как Pick<T, Exclude<keyof T, "other" | "props">> или, что то же самое, Omit<T, "other" | "props">. Использование условного типа Exclude будет нормально работать для потребителей этой функции, если T полностью разрешено (ie не является универсальным параметром типа c), но внутри функции машинописный текст не может на самом деле рассуждать о тип, который содержит Exclude. Это всего лишь ограничение того, как работают условные типы. Вы исключаете из T, но, поскольку T неизвестно, ts отложит оценку условного типа. Это означает, что T не может быть назначен на Pick<T, Exclude<keyof T, "other" | "props">>

Мы можем использовать утверждение типа, как у вас, и это то, что я рекомендовал в прошлом. Следует избегать утверждений типа, но они помогут вам, когда у вас (ie разработчик) больше информации, чем у компилятора. Это один из тех случаев.

Для лучшего обходного пути мы могли бы использовать хитрость. Хотя Omit<T, "props"> нельзя присвоить T, оно присваивается себе. Таким образом, мы можем набрать реквизиты компонента как Props | Omit<Props, "withPaper">. Поскольку Props и Omit<Props, "withPaper">, по сути, одного и того же типа, это не будет иметь большого значения, но это позволит компилятору назначить объект rest компонентным компонентам.


export const withOptionalPaper = <Props extends object>(
  Component: ComponentType<Props | Omit<Props & WithOptionalPaperProps, keyof WithOptionalPaperProps>>
) => ( {withPaper, ...otherProps }: Props & WithOptionalPaperProps) => {
  if (withPaper) {
    return (
      <Paper>
        <Component {...otherProps} />
      </Paper>
    );
  }
  return <Component {...otherProps } />;
};

Playground Link

...