Implementing the getNewBookDetails method using keyof and typeof

The getNewBookDetails method will simply retrieve values for the different fields in the new book creation form (in a specific book collection) and will create a Book object corresponding to those values.

Let's add the code for it.

First, add the method and some defensive checks:

getNewBookDetails(collectionIdentifier: string): { error?: string, book?: Book } { 
if (!collectionIdentifier) { // we throw this one because it means that there is a bug! throw new Error("The collection identifier must be
provided!"); } // required const newBookForm = document.getElementById(`newBook-$
{collectionIdentifier}`) as HTMLFormElement; if (!newBookForm) { throw new Error(`Could not find the new book form for
collection ${collectionIdentifier}`); } // build upon standard HTML DOM validation if (newBookForm.checkValidity() === false) { newBookForm.reportValidity(); return { error: "The new book form is invalid!" }; }
// Continue here }

Then, retrieve the different DOM elements:

    // from here on out, no need to check the validity of the specific 
//form fields // we just need to check if the fields can be found const newBookNameField = document.getElementById(`newBookName-$
{collectionIdentifier}`) as HTMLInputElement; if (!newBookNameField) { throw new Error("The new book form's name input was not found!
Did the template change?"); } const newBookAuthorField = document.getElementById
(`newBookAuthor-${collectionIdentifier}`) as HTMLInputElement; if (!newBookAuthorField) { throw new Error("The new book form's author input was not
found! Did the template change?"); } const newBookGenreSelect = document.getElementById
(`newBookGenre-${collectionIdentifier}`) as HTMLSelectElement; if (!newBookGenreSelect) { throw new Error("The new book form's genre select was not
found! Did the template change?"); } const newBookPagesField = document.getElementById(`newBookPages-
${collectionIdentifier}`) as HTMLInputElement; if (!newBookPagesField) { throw new Error("The new book form's page input was not found!
Did the template change?"); } // optional const newBookPictureField = document.getElementById
(`newBookPicture-${collectionIdentifier}`) as HTMLInputElement; if (!newBookPictureField) { throw new Error("The new book form's picture input was not
found! Did the template change?"); } const newBookDescriptionField = document.getElementById
(`newBookDescription-${collectionIdentifier}`) as
HTMLTextAreaElement; if (!newBookDescriptionField) { throw new Error("The new book form's description input was not
found! Did the template change?"); } // Continue here

Finally, create the object and return it:

const newBookGenre = Genre[newBookGenreSelect.value as keyof typeof Genre]; 
 
const newBookNumberOfPages = Number.parseInt(newBookPagesField.value); 
 
return { 
    book: new Book(newBookNameField.value, 
newBookDescriptionField.value, newBookPictureField.value,
newBookGenre, newBookAuthorField.value, newBookNumberOfPages) };

First, we check the validity of the whole form and report to the user if something is wrong.

Note that we also return a new object including the optional error property that we defined in our custom return type. This is what we meant when we mentioned the Node.js way of handling exceptions earlier.

By doing this, the calling code will be able to easily check for the presence of an error and react accordingly. In this case, this is much cleaner and less surprising than throwing an exception.

Then the major part of the code simply retrieves the DOM nodes that we expect to find and raise exceptions if they're not there (as this would be due to bugs and/or major changes in our code).

The way we retrieve the book's genre is also interesting:

const newBookGenre = Genre[newBookGenreSelect.value as keyof typeof Genre]; 

What we are doing with the preceding code is retrieving the Genre enum entry corresponding to the value selected by the user in the <select> element of the form.

In the code, you can see that we use both the keyof and typeof keywords. The reason for this is that, if we simply tried to use Genre[newBookGenreSelect.value], the TypeScript compiler would raise an error (https://stackoverflow.com/questions/36316326/typescript-ts7015-error-when-accessing-an-enum-using-a-string-type-parameter) because the string is arbitrary and TypeScript cannot know for sure that it will correspond to an existing entry of the Genre enum.

For those familiar with C# or .NET, this will be disappointing because it isn't safe: we tell TypeScript that this should be okay so that it stops complaining, but we cannot be sure either!

You can find a great explanation of the keyof typeof <type> trick at https://github.com/Microsoft/TypeScript/issues/14106#issuecomment-280253269. The short version implies that, if we had only used keyof Genre, we would have obtained a union of the numeric values behind our enum entries, whereas we are rather interested in the string values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.163.58