how tight should types be?

I have a tasker field in my task type. The most "accurate" type would look like -
type tasker =
{
_id: ObjectId;
name: string;
email: string;
}
| {
email: string;
}
| {
persona: string;
};
type tasker =
{
_id: ObjectId;
name: string;
email: string;
}
| {
email: string;
}
| {
persona: string;
};
However, I get errors like Property '_id' does not exist on type '{ email: string; }'. even after I am checking as follows if (tasker._id) {}. This is quite annoying and tbh I am not even sure how to get around it. On the other hand I could type tasker in the following way -
type tasker =
{
_id?: ObjectId;
name?: string;
email?: string;
persona?: string
}
type tasker =
{
_id?: ObjectId;
name?: string;
email?: string;
persona?: string
}
but this doesn't feel right. What should I be doing?
7 Replies
Ramazan
Ramazan14mo ago
You need to add _id and name to the other two union members I believe
jakemstar
jakemstar14mo ago
You want to do some sort of type narrowing so you can be certain that the properties exist. Here's the TS docs on that. https://www.typescriptlang.org/docs/handbook/2/narrowing.html One option is to create a common property on all these types in the union and check that value like this:
type Tasker =
{
_id: ObjectId;
name: string;
email: string;
kind: "someIdentifier"
}
| {
email: string;
kind: "anotherIdentifier";
}
| {
persona: string;
kind: "yetAnother";
};
type Tasker =
{
_id: ObjectId;
name: string;
email: string;
kind: "someIdentifier"
}
| {
email: string;
kind: "anotherIdentifier";
}
| {
persona: string;
kind: "yetAnother";
};
function getTaskerName(tasker: Tasker) {
if (tasker.kind === "someIdentifier") return tasker.name
}
function getTaskerName(tasker: Tasker) {
if (tasker.kind === "someIdentifier") return tasker.name
}
Documentation - Narrowing
Understand how TypeScript uses JavaScript knowledge to reduce the amount of type syntax in your projects.
jairrard
jairrard14mo ago
Why can't I use if (tasker._id) {} for the narrowing? Shouldn't that be enough? feels like unnecessary backend bloat to need to add a new field
Sybatron
Sybatron14mo ago
you don't have _id in all objects so ts can't be sure you gonna give him the first kind of object from the type
jairrard
jairrard14mo ago
Strange. Isn't the basis of narrowing meant to be that some of the other types don't have it? Logically if _id does exist the type should be
{
_id: ObjectId;
name: string;
email: string;
}
{
_id: ObjectId;
name: string;
email: string;
}
Sybatron
Sybatron14mo ago
function getTaskerName(tasker: Tasker) {
if ("_id" in tasker && tasker._id === 123) {
console.log("We have id");
}
}
function getTaskerName(tasker: Tasker) {
if ("_id" in tasker && tasker._id === 123) {
console.log("We have id");
}
}
another way is to do this... but here if you have 2 of them with _id you gonna have possible 2 objects instead of only one with the kind prop and _id can be different types in all of them which still sounds non type-safe to me
jairrard
jairrard14mo ago
ok yeah I think this makes sense. It is an issue since _id could exist as different types in each option in the union and so it not possible to use it for type narrowing Hmmmm