I have been bumping my head here and there and I couldn't gain any more progress than this
for example, I want TObject.props to accept 'href'
or 'download'
only if I pass TObject.name = 'a'
and not If passed TObject.name = 'p'
and also how can I make reduxStoreType.objects accept an array of TObjects but not necessarily all of them should be one type like Tobjects<'a'>
for example I want the possibility to have the following
reduxStoreType.objects = [TObject<'a'>,TObject<'p'>,TObject<'div'>...]
here are my interfaces
interface TagsTypes {
// HTML
p: React.HTMLAttributes<HTMLParagraphElement>;
a: React.AnchorHTMLAttributes<HTMLAnchorElement>;
form: React.FormHTMLAttributes<HTMLFormElement>;
head: React.HTMLAttributes<HTMLHeadElement>;
img: React.ImgHTMLAttributes<HTMLImageElement>;
input: React.InputHTMLAttributes<HTMLInputElement>;
span: React.HTMLAttributes<HTMLSpanElement>;
button: React.ButtonHTMLAttributes<HTMLButtonElement>;
div: React.HTMLAttributes<HTMLDivElement>;
}
interface TObject<T extends keyof TagsTypes> {
width: number;
height: number;
name: T;
props: TagsTypes[T];
innerText: string;
}
interface CanvasType extends HTMLDivElement {}
interface reduxStoreType {
canvas: CanvasType;
objects: TObject<'a'>;
}
CodePudding user response:
Let's reduce your problem to something simpler, solve the simpler version, and then hopefully you can apply it to your real-world scenario.
We have some shapes:
type ShapeTypes = "circle" | "square" | "rectangle" | "triangle";
And we could express the objects like this (similar to your current TObject definition):
interface Shape<Type extends ShapeTypes> {
type: Type;
area: number;
}
But then here's the problem. What if we want a circle to have a radius or diameter? A square to have a side length? A triangle's classification?
Here's where we use a discriminated union:
type Shape =
{
type: "circle";
area: number;
radius: number;
} | {
type: "square";
area: number;
side: number;
} | { ... }; // more
As you can see now, each type of shape has properties specific to them. Although this is a lot more typing it's what you want. You'll see when we use them like this:
if (shape.type === "circle") {
shape.radius // number, no errors
shape.side // error!
} else if (shape.type === "square") {
shape.radius // error!
shape.side // number, no errors
} else if (...) { ... } // more if you want
And also using these in an array is as simple as Shape[]
, while still retaining all this type information.
If it's too long to type all the redundant shared properties, make a new base type and intersect it for each member in the union:
type Base = {
area: number;
// more properties if needed
};
type Shape =
{
type: "circle";
radius: number;
} & Base | {
// ...
} | { ... }; // others
So now how do you apply this to your case?
Here's some code to get started:
type TBaseObject = {
// ...
};
type TObject = {
// ...
} | {
// ...
}; // ...
interface reduxStoreType {
canvas: CanvasType;
objects: TObject[];
}