I need to create an interface that is either a string or an object with one of three keys.
Basically I have a function that depending on the error returns something:
export const determineError = (error: ServerAlerts): AlertError => {
if (typeof error !== "string") {
if (error.hasOwnProperty("non_field_errors")) {
return error.non_field_errors[0];
} else if (error.hasOwnProperty("detail")) {
return error.detail;
} else if (error.hasOwnProperty("email")) {
return error.email[0];
} else {
return UNKNOWN_ERROR;
}
} else {
return error;
}
};
Here are the types:
export type AlertError =
| "Unable to log in with provided credentials."
| "E-mail is not verified."
| "Password reset e-mail has been sent."
| "Verification e-mail sent."
| "A user is already registered with this e-mail address."
| "Facebook Log In is cancelled."
| string;
export interface ServerAlerts {
non_field_errors: [string];
detail: string;
email: [string];
}
But the way I designed ServerAlerts
here does not work for me, since ServerAlerts can also be a string
and if it has one of its keys, it only has one.
How would you design such a type or interface?
EDIT: I tried making the keys optional by giving them a question mark, but then my linter complains in the respective key's error return statement in determineError
.
If I'm understanding you correctly, just declare the parameter as being either ServerAlerts
or string
:
export const determineError = (error: ServerAlerts|string): AlertError => {
// -----------------------------------------------^^^^^^^
In a comment you've said that all three of the ServerAlerts
properties are optional, so you need to mark them as such with ?
:
interface ServerAlerts {
non_field_errors?: [string];
detail?: string;
email?: [string];
}
However, that means that anything typed object
will also work, because all the fields are optional. So if you do both of those things, you get:
determineError("foo"); // Works
determineError({ non_field_errors: ["x"] }); // Works
determineError({ detail: "x" }); // Works
determineError({ email: ["x"] }); // Works
determineError({}); // Works (because all fields are optional)
let nonLiteralServerAlerts: object;
nonLiteralServerAlerts = { foo: ["x"] };
determineError(nonLiteralServerAlerts); // Works (because all fields are optional)
determineError({ foo: ["x"] }); // Fails (correctly)
Which suggests you might just use object
in the parameter signature. If you want to require one of the three fields (which I believe would do away with that UNKNOWN_ERROR
branch), you'd define three interfaces and make ServerAlerts
a union of them:
interface ServerAlertsNonFieldErrors {
non_field_errors: [string];
}
interface ServerAlertsDetail {
detail: string;
}
interface ServerAlertsEmail {
email: [string];
}
type ServerAlerts = ServerAlertsNonFieldErrors | ServerAlertsDetail | ServerAlertsEmail;
Then you'd use type assertions when returning the specific field:
if (error.hasOwnProperty("non_field_errors")) {
return (error as ServerAlertsNonFieldErrors).non_field_errors[0];
// ------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you do that, then you get:
determineError("foo"); // Works
determineError({ non_field_errors: ["x"] }); // Works
determineError({ detail: "x" }); // Works
determineError({ email: ["x"] }); // Works
determineError({}); // Fails (correctly)
let nonLiteralServerAlerts: object;
nonLiteralServerAlerts = { foo: ["x"] };
determineError(nonLiteralServerAlerts); // Fails (correctly)
determineError({ foo: ["x"] }); // Fails (correctly)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.