I don't understand why the transpiler is complaining. Here are the basic type declarations:
export enum SvgElementType {
path = "path",
circle = "circle",
}
export type SvgElement = {
id: number;
type: SvgElementType;
};
export type SvgPathElement = SvgElement & {
type: SvgElementType.path;
d: string;
};
export type SvgCircleElement = SvgElement & {
type: SvgElementType.circle;
cx: number;
cy: number;
radius: number;
};
export type Serializer<E extends SvgElement = SvgElement> = (element: E) => string;
const pathSerializer: Serializer<SvgPathElement> = e => "";
const circleSerializer: Serializer<SvgCircleElement> = e => "";
const serializers: Set<Serializer> = new Set();
serializers.add(pathSerializer); // <<<--- transpile error
// full TS error
// Argument of type 'Serializer<SvgPathElement>' is not assignable to parameter of type 'Serializer<SvgElement>'.
// Type 'SvgElement' is not assignable to type 'SvgPathElement'.
// Property 'd' is missing in type 'SvgElement' but required in type '{ type: SvgElementType.path; d: string; }'.ts(2345)
the only way I found was to modify the declaration of the Serializer
with any
as default type:
export type Serializer<E extends SvgElement = any> = (element: E) => string;
This flags me that there is probably a better way to preserve the minimum typing for the serializers
Set iterator usage later on...
The transpiler complains because you're effectively saying that you want a function that takes an SvgElement
as input, but then you're trying to assign to it a function that takes an SvgPathElement
.
This can't work - it would allow you to pass an SvgElement
to your function that wants an SvgPathElement
.
The formal term for this is "function parameters are contravariant".
Your code will require runtime type checks against the object being serialised anyway, so should you perhaps redefine your Serializer
type without the generic? e.g.:
export type Serializer = (element: SvgElement) => string;