首页 > 解决方案 > 我可以创建一个继承另一个 prop 的泛型类型的 prop 吗?

问题描述

我正在使用 TypeScript 编写一个 React 项目,并且我想创建一个新组件,该组件接受一个对象数组的值,并且可以创建新对象以添加到该数组中。创建新对象时,我希望它从我定义的蓝图开始,其中已经包含一些对象字段的默认值。

我的问题是我似乎无法让蓝图遵循值的通用类型。根据 TypeScript,以下(非常简化的示例)代码非常好:

type Props<T extends object> = {
  value: Array<T>;
  blueprint: T;
};

function MyComponent<T extends object>({}: Props<T>) {
  return <div></div>;
}

function ParentComponent() {
  return <MyComponent
    value={[{ foo: "bar" }, { foo: "oof"}]}
    blueprint={{ boo: "faz" }} // This doesn't throw a type-error, but I would like it to.
  />
}

blueprint当道具看起来不像数组中的对象时,我希望 TypeScript 引发类型错误value。我已经尝试通过使用扩展或等于第一个的第二个泛型类型来做到这一点,但我没有设法找到解决方案。

标签: reactjstypescript

解决方案


默认情况下,编译器将从属性和属性进行推断Tvalue通常blueprint作为从每个属性推断的类型的联合。但是您希望编译器仅从中推断Tvalue验证是否匹配blueprint。换句话说,您希望Tinblueprint成为非推理类型参数用法,如microsoft/TypeScript#14829中所建议的那样。

此功能尚未得到正式支持,但有几种方法可以实现此行为。第一个是通过与近乎顶级的类型相交来“降低属性T中的推理优先级” :blueprint{}

type NoInfer<T> = T & {};

type Props<T extends object> = {
  value: Array<T>;
  blueprint: NoInfer<T>;
};

另一种方法是使用条件类型延迟来防止发生推理:

type NoInfer<T> = [T][T extends any ? 0 : never];

type Props<T extends object> = {
  value: Array<T>;
  blueprint: NoInfer<T>;
};

在任一版本中,blueprint最终都被评估为 type T(或 Nearly T),但推理要么被延迟,要么被阻止。这两个版本都为您的示例提供了以下行为:

function ParentComponent() {
  return <MyComponent
    value={[{ foo: "bar" }, { foo: "oof" }]}
    blueprint={{ boo: "faz" }} // error!
  // Type '{ boo: string; }' is not assignable to type '{ foo: string; }'.
  />
}

这就是你想要的。


不确定是否值得做这些变通办法,因为它们可能在更广泛的代码库中存在一些可观察到的问题或边缘情况。这可能由您来决定您的用例有多严重需要无推断。好的,希望有帮助;祝你好运!

Playground 代码链接


推荐阅读