如何缩小SVG元素联合的类型

dag*_*da1 5 svg typescript reactjs

我使用反应以设置为可能是一个SVG元素的引用<rect><polygon><ellipse>

我有这个声明:

const shapeRef = useRef<SVGPolygonElement | SVGEllipseElement | SVGRectElement>(null);
Run Code Online (Sandbox Code Playgroud)

但是当我尝试在这样的<ellipse>元素上设置它时:

<ellipse
  cx={width / 8}
  cy={-sideDimension(y) / 8}
  rx={width}
  ry={height}
  ref={shapeRef}
/>
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

类型'RefObject'不能分配给类型'string | ((实例:SVGEllipseElement | null)=> void)| RefObject | 空| 未定义”。类型“ RefObject”不可分配给类型“ RefObject”。输入'SVGPolygonElement | SVGEllipseElement | SVGRectElement'不可分配给'SVGEllipseElement'类型。类型“ SVGPolygonElement”缺少类型“ SVGEllipseElement”的以下属性:cx,cy,rx,ryts(2322)

我的理解是,为了某种方法我必须以某种方式缩小类型,否则使用此引用的每个对象都必须具有联合的所有属性。

Tha*_*guy 8

你是对的。Typescript会给您该错误,因为它不知道应将哪种类型解释shapreRef为。

IMO最好的解决方案是使用Type Guard。一类保护是检查是否一个变量是某种类型的打字稿方式。对于联合类型,它使打字稿理解某些东西是特定类型的。

例如,在您的情况下,可能是这样的:

interface IEllipse {
  attr1: string;
  attr2: string;
}

interface IRect {
  attr3: string;
  attr4: string;
}

type SvgShape = IEllipse | IRect | IPolygon;

function isEllipse(shape: SvgShape): shape is IEllipse {
    return (shape as IEllipse).attr1 !== undefined;
}

Run Code Online (Sandbox Code Playgroud)

请注意,返回类型为shape is IEllipse。这意味着打字稿将在此处解释真实的返回值,就好像shape 是一个IEllipse

然后,无论您在哪里使用SvgShape,都可以检查它的类型,SvgShape而打字稿应该基于该类型知道该类型:

// ...
render() {
  const shape: SvgShape = this.getCurrentShape();

  if (isEllipse(shape)) {
    // typescript should KNOW that this is an ellipse inside this if
    // it will accept all of Ellipse's attribute and reject other attributes
    // that appear in other shapes

    return <ellipse .../>;
  } else if (isRect(shape)) {
    // typescript should interpet this shape as a Rect inside the `if`

    return <rect ... />;
  } else {
    // typescript will know only one subtype left (IPolygon)

    return <polygon points="..." />;
  }
}
// ...
Run Code Online (Sandbox Code Playgroud)

为什么不只是交集类型?

好吧...相交类型更适用于每种类型(Rect,Polygon等)在新项目中具有完全相同的属性的情况。例如:

type Inter = IRect & IPolygon & IEllipse;
Run Code Online (Sandbox Code Playgroud)

表示Inter类型为IRectand IPolygonIEllipse。这意味着此类型的对象将具有所有三种类型的所有成员。因此,尝试访问实际上是的形状points上的属性(存在于IPolygonIRect,就好像该属性存在于其中(我们不想要)

您将大体上看到用于混合的交集类型和其他经典面向对象模型不适合的概念。

如何与useRef一起使用?

type SvgShape = SVGPolygonElement | SVGEllipseElement | SVGRectElement;

const shapeRef = useRef<SvgShape>(null);

function isEllipseRef(shapeRef: MutableRefObject<SvgShape>): shapeRef is MutableRefObject<IEllipse> {
  const shape: SvgShape = shapeRef.current;
  return (shape as IEllipse).attr1 !== undefined;
}
Run Code Online (Sandbox Code Playgroud)