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

IT技术 reactjs typescript svg
2021-04-27 19:33:57

我正在使用 react 来设置对 svg 元素的引用,该元素可能是<rect>,<polygon><ellipse>.

我有这个声明:

const shapeRef = useRef<SVGPolygonElement | SVGEllipseElement | SVGRectElement>(null);

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

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

我收到此错误:

类型 'RefObject' 不能分配给类型 'string | ((例如:SVGEllipseElement | null) => void) | 引用对象 | 空| 不明确的'。类型“RefObject”不可分配给类型“RefObject”。输入'SVGPolygonElement | SVGEllipseElement | SVGRectElement' 不能分配给类型 'SVGEllipseElement'。“SVGPolygonElement”类型缺少“SVGEllipseElement”类型中的以下属性:cx、cy、rx、ryts(2322)

我的理解是,我需要以某种方式缩小类型以使其工作,否则使用此 ref 的每个对象都必须具有联合的所有属性。

1个回答

你是对的。typescript给你这个错误,因为它不知道它应该将哪一种类型考虑在内shapreRef

IMO 的最佳解决方案是使用Type Guards类保护是检查是否一个变量是某种类型的typescript方式。对于联合类型,这让 typescript 理解某些东西属于特定类型。

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

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;
}

请注意,返回类型是shape is IEllipse. 这意味着typescript将在这里解释一个真实的返回值,就好像shape 是一个IEllipse.

然后,无论您想在何处使用 a SvgShape,您都可以检查SvgShape它是哪种类型,并且typescript应该基于此知道类型:

// ...
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="..." />;
  }
}
// ...

为什么不只是一个 Intersection 类型?

嗯...交集类型更多用于每种类型(矩形、多边形等)在新项目中具有完全相同的属性的情况。例如:

type Inter = IRect & IPolygon & IEllipse;

意味着一个Inter类型是IRectIPolygonIEllipse这意味着这种类型的对象将具有所有三种类型的所有成员。因此,尝试访问实际上是 的形状points的属性(存在于 上IPolygonIRect将表现得好像该属性存在于那里(我们不想要)

您将主要看到用于 mixin 和其他不适合经典面向对象模型的概念的交集类型。

如何与 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;
}