typescript 我们如何在React组件中使用泛型,从哈希Map派生类型?

3ks5zfa0  于 2023-06-07  发布在  TypeScript
关注(0)|答案(1)|浏览(150)

我有一个项目,我在应用程序的几个地方使用基本相同的组件。我正试图通过创建一个支持这些现有用例通用组件来删除一些冗余代码。这些组件中的每一个都使用从相同的基本类型扩展的Type,我想在我的组件中提供一个“type”值作为始终提供的prop,然后在hashmap中使用这个“type”来确定这个组件应该使用什么prop。然而,当我使用我的“类型”属性来确定组件的属性时,我遇到了类型问题。
为了支持多种类型,我创建了一个枚举,其中所有值都是“type”值。

export enum Shape{
    SQUARE = "square",
    CIRCLE = "circle",
    RECTANGLE = "rectangle"
}

然后,我为这些类型创建了接口,并创建了一个哈希Map来将枚举值链接到该类型。

export interface SquareComponent{
    side: number;
    color: string;
}

export interface CircleComponent{
    radius: number;
    color: string;
}

export interface RectangleComponent{
    length: number;
    height: number;
    color: string;
}

//Hash map to get the type using an enum value
export type ShapeConversion = {
    [T in Shape]: {
        [Shape.SQUARE]: SquareComponent;
        [Shape.CIRCLE]: CircleComponent;
        [Shape.RECTANGLE]: RectangleComponent;
    }[T]
}

接下来,我用泛型为ReactComponent创建了接口。

export interface myComponentProps<T>{
    type: Shape, 
    shapeList: T[]
}

因此,当我创建我的组件时,类型可以很好地与我定义的一个接口(如CircleComponent)一起工作

//This is OK
const myComponent = ({type, shapeList}:myComponentProps<CircleComponent>)=>{
   // does something with shapes
}

当我使用ShapeConversion散列Map创建组件并直接指定形状时,它也能正常工作。

//This is also OK
const myComponent2 = ({type, shapeList}:myComponentProps<ShapeConversion[Shape.CIRCLE]>)=>{
    // does something with shapes
 }

然而,我希望能够使用提供的类型属性来确定ShapeConversion散列Map的结果,但这不起作用。

//This is not Ok
// Gives a typescript error "Type 'any' cannot be used as an index type."
const myComponent3 = ({type, shapeList}:myComponentProps<ShapeConversion[typeof type]>)=>{
    // does something with shapes
 }

有没有一种方法可以使用一个总是提供的prop,比如type,来确定泛型组件的prop?

i86rm4rw

i86rm4rw1#

我建议将MyComponentProps更改为接受一个形状,而不是MyComponentProps.shapeList的类型。这将允许我们使用ShapeConversiontype的值与shapeList连接起来:

type MyComponentProps<T extends Shape> = {
  type: T;
  shapeList: ShapeConversion[T][];
};

但是,如果你不想指定形状手册,那么我们应该使用union distribution创建一个可能 prop 的联合:

type MyComponentProps<T extends Shape = Shape> = T extends T
  ? {
      type: T;
      shapeList: ShapeConversion[T][];
    }
  : never;

示例:

// {
//     type: Shape.CIRCLE;
//     shapeList: CircleComponent[];
// }
type CircleProps = MyComponentProps<Shape.CIRCLE>;

// {
//     type: Shape.CIRCLE;
//     shapeList: CircleComponent[];
// } | {
//     type: Shape.SQUARE;
//     shapeList: SquareComponent[];
// } | {
//     type: Shape.RECTANGLE;
//     shapeList: RectangleComponent[];
// }
type AnyPossibleProps = MyComponentProps;

用途:

//This is OK
const myComponent = ({ type, shapeList }: MyComponentProps<Shape.CIRCLE>) => {
  // does something with shapes
};

//This is OK
const myComponent2 = ({ type, shapeList }: MyComponentProps) => {
  // does something with shapes
};

// no error
myComponent2({
  type: Shape.CIRCLE,
  shapeList: [
    {
      radius: 1,
      color: 'ref',
    },
  ],
});

// expected error
myComponent2({
  type: Shape.CIRCLE,
  shapeList: [
    {
      side: 1,
      color: 'ref',
    },
  ],
});

Playground

相关问题