如何在ts中使用拓展运算符组合泛型变量?

我写了一个react 高阶组件 简化后形如


type Props<T> = {
  value: T
}

function HOC<P extends Props<any>>(Comp: ComponentType<P>) {
  type ValueType = P extends Props<infer T> ? T : never
  type Example = Omit<P, 'value'> & { value1: ValueType }

  return function CompWithHOC(props: Example) {
    const { value1, ...rest } = props
    const compProps: P = { // here
      ...rest,
      value: value1
    }
    return <Comp {...compProps}></Comp>
  }
}

但 compProps处有类型错误 不能将字面量识别为P 报错为
不能将类型“Omit<Example, "value1"> & { value: ValueType; }”分配给类型“P”。
"Omit<Example, "value1"> & { value: ValueType; }" 可赋给 "P" 类型的约束,但可以使用约束 "Props<any>" 的其他子类型实例化 "P"。

我希望学习如何在ts中使用拓展运算符展开和组合变量 以及高阶组件的写法

补充:
1.上述代码是简化后的结果 仅取基本含义 不代表实际内容
2.P extends Props<any>是必要的 因为被修饰的组件可能具有其他参数 且我希望能从参数中自动获取类型(目前还没找到更好的写法)
3.ai建议的rest as Omit<P, 'value'>已经试过 没有意义.目前尝试这一行上的类型断言都不生效

阅读 899
avatarAI BotBETA

在 TypeScript 中,当你使用高阶组件(HOC)并且涉及到泛型时,确保类型的正确性和兼容性是非常重要的。在你的例子中,你遇到了一个类型推断的问题,这是因为 TypeScript 的类型系统无法直接确定 P 的确切类型,尤其是在你尝试将 props 转换为 P 类型时。

问题在于 rest 的类型是 Omit<Example, "value1">,它只保证了除 value1 外的所有属性都存在于 P 中,但并没有保证 P 可能包含的其他非 value 属性也存在。此外,ValueType 是从 P 推断出来的,但它可能与 Pvalue 的实际类型不完全一致(尽管在你的例子中,由于 P 继承自 Props<any>,这通常不是问题)。

要解决这个问题,你可以通过显式地转换 restvalue1 来创建一个新的对象,这个对象的类型兼容 P。这里的关键是理解 TypeScript 的类型兼容性是基于结构子类型的,所以只要对象的形状匹配,就可以被当作另一种类型。

这里是一个修改后的版本,它使用了类型断言来确保类型兼容性:

import React, { ComponentType, PropsWithChildren } from 'react';

type Props<T> = {
  value: T;
}

function HOC<P extends Props<any>>(Comp: ComponentType<P>) {
  type ValueType = P extends Props<infer T> ? T : never;
  type Example = Omit<PropsWithChildren<P>, 'value'> & { value1: ValueType }; // 使用 PropsWithChildren 以支持 children

  return function CompWithHOC(props: Example) {
    const { value1, ...rest } = props;

    // 创建一个新对象,其类型被断言为 P
    const compProps = {
      ...rest as Omit<P, 'value'>, // 显式地断言 rest 为 P 中除 value 外的部分
      value: value1 as P['value']  // 显式地断言 value1 为 P 中 value 的类型
    } as P; // 最后将整个对象断言为 P 类型

    return <Comp {...compProps} />;
  }
}

// 使用示例
interface MyComponentProps extends Props<string> {
  extraProp: number;
}

const MyComponent: React.FC<MyComponentProps> = ({ value, extraProp }) => (
  <div>{value} - {extraProp}</div>
);

const EnhancedMyComponent = HOC(MyComponent);

// 渲染
const App = () => (
  <EnhancedMyComponent value1="Hello" extraProp={42} />
);

注意几个关键点:

  1. 我使用了 PropsWithChildren 而不是 P 直接在 Omit 中,这是因为高阶组件通常应该支持子元素(children)。
  2. 我添加了类型断言来确保 restvalue1 能够正确地被当作 P 类型的一部分。
  3. 在最后的对象上我也使用了类型断言 as P,以确保整个对象符合 P 的类型要求。

这样修改后,你的高阶组件应该能够正确地处理泛型类型 P,并且在 TypeScript 中不会出现类型错误。

1 个回答
type Props<T> = {
    value: T
}

function HOC<T>(Comp: ComponentType<Props<T>>) {
    type Example = { value1: T }

    return function CompWithHOC(props: Example) {
        const { value1, ...rest } = props
        const compProps: Props<T> = {
            ...rest,
            value: value1
        }
        return <Comp {...compProps}></Comp>
    }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Microsoft
子站问答
访问
宣传栏