Skip to content

Commit

Permalink
2024-02-22: Supernatural: I’ll interrogate the code
Browse files Browse the repository at this point in the history
- consistent structure
- fix explanation comments
  • Loading branch information
e-kulikov committed Mar 1, 2024
1 parent 11d8e58 commit a55ce15
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 33 deletions.
42 changes: 26 additions & 16 deletions 2024-02-22_superstitious-programming/1-undefined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,43 @@ const customer: { dob?: Record<string, any> } = {};
// 1. Object property check
{
// before
if (typeof customer.dob !== 'undefined') {
customer.dob.constructed = new Date().toLocaleDateString();
{
if (typeof customer.dob !== 'undefined') {
customer.dob.constructed = new Date().toLocaleDateString();
}
}

// after
if (customer.dob !== undefined) {
customer.dob.constructed = new Date().toLocaleDateString();
{
if (customer.dob !== undefined) {
customer.dob.constructed = new Date().toLocaleDateString();
}
/*
It used to be that `undefined` was a global variable that could be reassigned.
Nowadays, it's impossible. Don't be superstitious, you shouldn't use bulky constructions with `typeof` checks
*/
}
/*
It used to be that `undefined` was a global variable that could be reassigned.
Nowadays, it's impossible. Don't be superstitious, you shouldn't use bulky constructions with `typeof` checks
*/
}

// 2. Global variable check
{
// before
if (typeof customerDoB !== 'undefined') {
customerDoB = new Date().toLocaleDateString()
{
if (typeof customerDoB !== 'undefined') {
customerDoB = new Date().toLocaleDateString()
}
}

// after
// there is no "after" for this construction ¯\_(ツ)_/¯, thanks to TypeScript
{
// there is no "after" for this construction ¯\_(ツ)_/¯, thanks to TypeScript

/*
There was a case when we check the value of global variable, that might not be declared, and that causes an error.
If you are not sure if the global variable exists, it's better check it explicitly (using `if ('varname' in global)`).
/*
There is a case when you want to check the value of global variable, that might not be declared, and that causes an error.
Don't be superstitious, TypeScript highlights cases like this and protects you from accessing non-declared variables.
Don't be superstitious, TypeScript highlights cases like this and protects you from accessing non-declared variables.
*/
* If you use JavaScript and are not sure if the global variable exists,
it's better check it explicitly (using `if ('varname' in global)`).
*/
}
}
4 changes: 2 additions & 2 deletions 2024-02-22_superstitious-programming/3-default-assign.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// before
{
// before
class HTTPError extends Error {
status: number;
constructor(status: number, message: string | undefined = undefined) {
Expand All @@ -14,8 +14,8 @@
*/
}

// after
{
// after
class HTTPError extends Error {
status: number;
constructor(status: number, message?: string) {
Expand Down
8 changes: 3 additions & 5 deletions 2024-02-22_superstitious-programming/4-return.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ type MiddlewareFunction = (req: Record<string, any>, res: unknown, next: (a?: Er
class UnauthorizedError extends Error {}
declare function validateToken(token: string): Promise<boolean>;

// before
{
// before
const authenticate : MiddlewareFunction = async (req, res, next) => {
try {
const isValid = await validateToken(req.headers.Authorization);
Expand All @@ -13,13 +13,11 @@ declare function validateToken(token: string): Promise<boolean>;
}
};

/*
Not a big problem, of course. But as much code you write, as many problems you'll have in the future, during refactoring.
*/
// Not a big problem, of course. But as much code you write, as many problems you'll have in the future, during refactoring.
}

// after
{
// after
const authenticate : MiddlewareFunction = async (req, res, next) => {
try {
const isValid = await validateToken(req.headers.Authorization);
Expand Down
24 changes: 14 additions & 10 deletions 2024-02-22_superstitious-programming/5-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ declare interface Queue extends Record<string, any> {
}
declare const queues: Record<string, Queue>

// before
{
// before
const readFromQueue = async (queueName: string) => {
try {
const data = await queues[queueName].read();
Expand All @@ -16,15 +16,17 @@ declare const queues: Record<string, Queue>

/*
My favourite so far.
When you declare an async function, you can think that everything inside the function is wrapped inside a big promise already.
When you declare an async function, you can think that everything inside the function is wrapped already inside a big promise.
Don't be superstitious, no need to return resolved promise from async function, when you want to return something.
You could think, that it means that now return value of the function is `Promise<Promise<any>>`,
but thanks to monad-based nature of Promises, it would be unfolded anyway into Promise<any>.
You could think, that it means that now return value of the function is `Promise<Promise<any>>`, and you meant to do that,
but because to monad-based nature of Promises, it would be unfolded anyway into Promise<any>.
*/
}

// after (option 1, full equivalent)
{
// after 1
const readFromQueue = async (queueName: string) => {
try {
return await queues[queueName].read();
Expand All @@ -36,25 +38,27 @@ declare const queues: Record<string, Queue>
// This is the full equivalent of the previous implementation, but without extra overhead unnecessary Promise.resolve()
}

// after (option 2, no error handler)
{
// after 2
/*
The previous implementation allows you to handle error on this low level.
If you have some top-level error handler, you can simplify the function even more.
*/
const readFromQueue = (queueName: string) => queues[queueName].read();

/*
As you can notice, I even omitted async in function declaration.
As you can see, I've even omitted async in function declaration.
It's because you don't need it: async means nothing but the function returns a promise,
but queue.read() returns a promise itself.
but queue.read() itself returns a promise already.
If you declare the function as async, it'd be again Promise<Promise<unknown>>,
and as we already know, it's unfolded into simple Promise<unknown>.
and as we already know, it's unfolded into simple Promise<unknown>.
*/
}

// after (option 3, one-liner with error handling)
{
// after 3
/*
If you like one-liners, but want to keep error-handling inside the function, you can write it this way
*/
Expand Down

0 comments on commit a55ce15

Please sign in to comment.