More Elegant Way For Type

Anyone know a better way to write this type? I feel like this is repetitive. Thx!
26 Replies
ippo
ippo2y ago
do yourself a favor and do not use numbers for roles or permissions use descriptive texts like "admin", "user", "guest", "manager" and so on working with numbers, looking them up and try to understand what they mean is just a pain
Perfect
Perfect2y ago
rip - its quite late for me to change that in our project, but I will keep it in mind we only have three (0, 1, 2) 2 owner, 1 instructor, 0 student But I do see your point
Scot
Scot2y ago
I second this, we used to have numbers thst corresponded to entry’s in a db where the name was kept It was awful Wasted so many engineer hours down the track
ippo
ippo2y ago
@Alex | C4 model stan this sounds to me like a forced artificial id for roles 🙂
Scot
Scot2y ago
It was because Java It used Java Emina Enums* Then in hibernate orm they would get numbers in the db Same with a lot of things It was terrible
ippo
ippo2y ago
I saw so many developers creating tables and adding an id column just because, even if they have a unique identifier
Jon Higger (He / Him)
Something like this could work
type BaseUser = {
id: number
name: string;
role: 0 | 1 | 2,
owner_name: string;
member_count: number
}

interface Admin extends BaseUser {
role: 1 | 2
code: string
}

interface RegularUser extends BaseUser {
role: 0
}

type CourseGeneral = Admin | RegularUser
type BaseUser = {
id: number
name: string;
role: 0 | 1 | 2,
owner_name: string;
member_count: number
}

interface Admin extends BaseUser {
role: 1 | 2
code: string
}

interface RegularUser extends BaseUser {
role: 0
}

type CourseGeneral = Admin | RegularUser
you could also do
type BaseUser = {
id: number
name: string;
role: 0 | 1 | 2,
owner_name: string;
member_count: number
}

type Admin = BaseUser & {
role: 1 | 2
code: string
}

type RegularUser = BaseUser & {
role: 0
}

type CourseGeneral = Admin | RegularUser
type BaseUser = {
id: number
name: string;
role: 0 | 1 | 2,
owner_name: string;
member_count: number
}

type Admin = BaseUser & {
role: 1 | 2
code: string
}

type RegularUser = BaseUser & {
role: 0
}

type CourseGeneral = Admin | RegularUser
I like the code style of version 2 more, but I think the type inference is a little better with the interfaces don't quote me on that though haha
Perfect
Perfect2y ago
Awesome, I will try these out, much appreciated! I am basically only gonna show the code to roles 1 and 2 so needed a type to express that Seems this will work great And the data fetch could be either ^
ippo
ippo2y ago
or something like this:
type CourseGeneralBase = {
id: number;
name: string;
owner_name: string;
member_count: number;
}

type CourseGeneral =
CourseGeneralBase & { code: string; role: 1 | 2; } |
CourseGeneralBase & { role: 0; };
type CourseGeneralBase = {
id: number;
name: string;
owner_name: string;
member_count: number;
}

type CourseGeneral =
CourseGeneralBase & { code: string; role: 1 | 2; } |
CourseGeneralBase & { role: 0; };
Perfect
Perfect2y ago
When I am doing it any of these ways, one of my components that could possibly render a code (if role is 1 or 2) is giving me this error.
Perfect
Perfect2y ago
Now I understand its trying to say its not guaranteed to exist, its basically an optional prop. Do i have to make a new type for this
Perfect
Perfect2y ago
Perfect
Perfect2y ago
Ah damn hold on ignore this I gotta change some stuff I am all over the place
Perfect
Perfect2y ago
What am I replacing exactly? do you mean change to 0?
Perfect
Perfect2y ago
Adding this fixed it for now 🙂 - just saying that code is potentially not going to be a prop
Perfect
Perfect2y ago
But then here, TS not saying code doesnt exist if role === 0
ippo
ippo2y ago
if I use role: 0, it is telling me that code: should not be there
Perfect
Perfect2y ago
Hmmm Well, dont feel like you have to read all this but this is my flow
type CourseGeneralBase = {
id: number;
name: string;
owner_name: string;
member_count: number;
};

type CourseGeneral =
| (CourseGeneralBase & { code: string; role: 1 | 2 })
| (CourseGeneralBase & { role: 0 });

// saying that data will be a CourseGeneral array
const { data, error, loading } = useClerkSWR<CourseGeneral[]>("/courses");

// filtering the array using my own function to avoid two calls to filter
const [ownedCourses, joinedCourses] = doubleFilter(
data,
(course) => course.role === 2
);

<Tab.Panel>
{ownedCourses.length === 0 && (
<p>You have not created any courses</p>
)}
{ownedCourses.map((curr) => (
<CourseCard key={curr.id} {...curr} />
))}
</Tab.Panel>
<Tab.Panel>
{joinedCourses.length === 0 && (
<p>You have not joined any courses</p>
)}
{joinedCourses.map((curr) => (
<CourseCard key={curr.id} {...curr} />
))}
</Tab.Panel>

// then this is the component prop types I am using ^
const CourseCard = ({
id,
name,
role,
code,
owner_name,
member_count,
}: CourseGeneral)
//rest of component
type CourseGeneralBase = {
id: number;
name: string;
owner_name: string;
member_count: number;
};

type CourseGeneral =
| (CourseGeneralBase & { code: string; role: 1 | 2 })
| (CourseGeneralBase & { role: 0 });

// saying that data will be a CourseGeneral array
const { data, error, loading } = useClerkSWR<CourseGeneral[]>("/courses");

// filtering the array using my own function to avoid two calls to filter
const [ownedCourses, joinedCourses] = doubleFilter(
data,
(course) => course.role === 2
);

<Tab.Panel>
{ownedCourses.length === 0 && (
<p>You have not created any courses</p>
)}
{ownedCourses.map((curr) => (
<CourseCard key={curr.id} {...curr} />
))}
</Tab.Panel>
<Tab.Panel>
{joinedCourses.length === 0 && (
<p>You have not joined any courses</p>
)}
{joinedCourses.map((curr) => (
<CourseCard key={curr.id} {...curr} />
))}
</Tab.Panel>

// then this is the component prop types I am using ^
const CourseCard = ({
id,
name,
role,
code,
owner_name,
member_count,
}: CourseGeneral)
//rest of component
I think it may have to do with the rest props, but TS is not wrong. the code prop will not always be sent because it may not even exist
ippo
ippo2y ago
I derived the types of your optional type that you pasted above just in case:, write it like this (use eslint with airbnb rules (write variable names small)
const courseCard: CourseGeneral = ({ ...
const courseCard: CourseGeneral = ({ ...
Perfect
Perfect2y ago
courseCard is a component, shouldn't that be in caps?
ippo
ippo2y ago
ahh okay, didn't know that it is a react component 🙂
Perfect
Perfect2y ago
Yeah sorry here it is
const CourseCard = ({
id,
name,
role,
code, // ERROR - Property 'code' does not exist on type 'CourseGeneral'.
owner_name,
member_count,
}: CourseGeneral & { code?: string }) => {
return (
<Link
href={`/courses/${id}`}
>
<p >{owner_name}</p>
<h3>{name}</h3>
<div>
<div>
<UserIcon />
<p>
{member_count} {member_count === 1 ? "Member" : "Members"}
</p>
</div>
{role === 2 || role === 1 ? (
<div >
<KeyIcon />
<p>{code}</p>
</div>
) : null}
</div>
</Link>
);
};
const CourseCard = ({
id,
name,
role,
code, // ERROR - Property 'code' does not exist on type 'CourseGeneral'.
owner_name,
member_count,
}: CourseGeneral & { code?: string }) => {
return (
<Link
href={`/courses/${id}`}
>
<p >{owner_name}</p>
<h3>{name}</h3>
<div>
<div>
<UserIcon />
<p>
{member_count} {member_count === 1 ? "Member" : "Members"}
</p>
</div>
{role === 2 || role === 1 ? (
<div >
<KeyIcon />
<p>{code}</p>
</div>
) : null}
</div>
</Link>
);
};
I guess for now I am just gonna add that extra optional part and manually check the role being 2 or 1 to display code or in turn, I know code will only be a string (not undefined) if role is 2 or 1 so I can just display code whenever it exists
Sybatron
Sybatron2y ago
that's because of the type narowing you need some prop like role to identify which of the 2 cases you want you need some conditional to check if role !== 0 then you try to use .code otherwise you get error
Jon Higger (He / Him)
You won’t be able to destructure the code field with this setup because now it may not be there in the args. But if you make it one object like “courseGeneral” then you can say “if the courseGenerals code is 1 or 2 then pull off the code field” and then typescript will be happy. This is actually an example of typescript doing something you want it to. You don’t want to accidentally call on the code field when it’s not there
Perfect
Perfect2y ago
@Sybatron @Jon Higger (He / Him) thanks guys! This solves it 🙏
Want results from more Discord servers?
Add your server