网站搜索

reactjs - 如何在 Typescript 中强制覆盖现有类型?


我正在使用 Formik,一个 React 表单库,并创建我自己的自定义字段。我依赖于 Formik 的通用 Schema 类型,它表示他们的 values 对象,该对象包含表单中所有字段的所有值。

自定义组件是接收 FormikProps 的 checkboxgroup 组件

type FormikProps<Schema> = {
  ...
  values: Schema
}

type Groceries = Array<{
  id: string;
  value: string;
}>

type Schema = {
  ...
  firstName: string;
  lastName: string;
  groceries: Groceries
}

type Props<Schema> = {
  ...
  name: keyof Schema & string;
} & FormikProps<Schema, unknown>

type State = {
  isBananaSelectedInGroceries: boolean;
}

const getInitialState = (props: Props) => {
  const groceries = props.values["groceries"];

  // Property 'map' does not exist on type 'Schema[keyof Schema & string]'.ts(2339)
  const values = groceries.map(({ value }) => value);
  ...
}

export class CheckboxGroup<Schema> extends React.Component<
  Props<Schema>,
  State
> {
  state = getInitialState(this.props);
  ...
}

在 getInitialState 中,TypeScript 抱怨 groceries 不是一个数组。所以为了强行覆盖类型,我尝试了这个方法:

  // Type 'Schema[keyof Schema & string]' is not assignable to type 'Groceries'.
  //     Type 'Schema[string]' is not assignable to type '{ id: string; value: string; }[]'.ts(2322)
  const groceries: Groceries = props.values["groceries"];
  const values = groceries.map(({ value }) => value);

我可以这样进行类型转换,然后错误就消失了——但这显然不是理想的做法:

  const groceries: Groceries = (props.values["groceries"] as unknown) as Groceries;
  const values = groceries.map(({ value }) => value);
  1. 这里发生了什么?为什么 TypeScript 没有推断出 values["groceries"] 是一个 Array?

  2. 在这里让 Typescript 满意的理想方法是什么?

答案#1

你的 TypeScript 声明中有一些错误。修复它们之后,一切都按预期工作:Playground 链接

主要问题是 const getInitialState=(props: Props),因为 Props 是一个泛型类型(但它不需要是泛型,至少在当前的实现中不需要)。

通常你应该只在需要动态更改它们时使用泛型(例如使用 T,而不是具体类型,因为那可能会造成混淆)。

type FormikProps = {
  values: Schema
}

type Groceries = Array<{
  id: string;
  value: string;
}>

type Schema = {
  firstName: string;
  lastName: string;
  groceries: Groceries
}

type Props = {
  name: keyof Schema & string;
} & FormikProps

type State = {
  isBananaSelectedInGroceries: boolean;
}

const getInitialState = (props: Props) => {
  const groceries = props.values["groceries"];
  const values = groceries.map(({ value }) => value);
}