Abstract: TypeScript and JavaScript have been growing steadily in recent years. There are some habits we’ve developed in the past when writing code, and some habits don’t make sense. Here are 10 bad habits we should all break.

Author: DanielBartholomae

Translation: Crazy geek

Original text: startup-cto.net/10-bad-type…

TypeScript and JavaScript have been growing steadily in recent years. There are some habits we’ve developed in the past when writing code, and some habits don’t make sense. Here are 10 bad habits we should all break.

1. Do not use strict mode

  • What does this habit look like

Tsconfig.json is not written in strict mode.

{
  "compilerOptions": {
    "target": "ES2015",
    "module": "commonjs"
  }
}
Copy the code
  • How should

Simply enable strict mode:

{
  "compilerOptions": {
    "target": "ES2015",
    "module": "commonjs",
    "strict": true
  }
}
Copy the code
  • Why do you have this bad habit

It takes time to introduce stricter rules into existing code bases.

  • Why shouldn’t you do that

Stricter rules make it easier to maintain code in the future, saving you a lot of time.

2. Use | | defined the default values

  • What does this habit look like

Use the old | | processing backup default values:

function createBlogPost (text: string, author: string, date? : Date) { return { text: text, author: author, date: date || new Date() } }Copy the code
  • How should

Use a new?? Operator, or redefine default values in arguments.

function createBlogPost (text: string, author: string, date: Date = new Date())
  return {
    text: text,
    author: author,
    date: date
  }
}
Copy the code
  • Why do you have this bad habit

?? Operators were introduced only last year, and can be difficult to set to parameter defaults when using values in long functions.

  • Why shouldn’t you do that

?? And | |,?????? Null or undefined only, not all virtual values.

3. Feel free to use any type

  • What does this habit look like

Use the any type when you are unsure of the structure.

async function loadProducts(): Promise<Product[]> {
  const response = await fetch('https://api.mysite.com/products')
  const products: any = await response.json()
  return products
}
Copy the code
  • How should

Change everything in your code that uses any to unknown

async function loadProducts(): Promise<Product[]> {
  const response = await fetch('https://api.mysite.com/products')
  const products: unknown = await response.json()
  return products as Product[]
}
Copy the code
  • Why do you have this bad habit

Any is handy because it basically disables all type checking. Often, any is used even in the officially provided types. For example, the TypeScript team sets the response.json() type in the example above to Promise.

  • Why shouldn’t you do that

It basically disables all type checking. Anything that comes in through any drops all type checking altogether. This will make errors harder to catch.

4. val as SomeType

  • What does this habit look like

Forcing a type that the compiler cannot infer.

async function loadProducts(): Promise<Product[]> {
  const response = await fetch('https://api.mysite.com/products')
  const products: unknown = await response.json()
  return products as Product[]
}
Copy the code
  • How should

This is where the Type Guard comes in.

function isArrayOfProducts (obj: unknown): obj is Product[] { return Array.isArray(obj) && obj.every(isProduct) } function isProduct (obj: unknown): obj is Product { return obj ! = null && typeof (obj as Product).id === 'string' } async function loadProducts(): Promise<Product[]> { const response = await fetch('https://api.mysite.com/products') const products: unknown = await response.json() if (! isArrayOfProducts(products)) { throw new TypeError('Received malformed products API response') } return products }Copy the code
  • Why do you have this bad habit

When moving from JavaScript to TypeScript, existing code bases often make assumptions about types that the TypeScript compiler can’t automatically infer. At this point, you can speed up the conversion with asSomeOtherType without having to change the Settings in tsconfig.

  • Why shouldn’t you do that

Type Guard ensures that all checks are unambiguous.

A. any B. any C. any D. any

  • What does this habit look like

Create incomplete use cases when writing tests.

interface User {
  id: string
  firstName: string
  lastName: string
  email: string
}

test('createEmailText returns text that greats the user by first name', () => {
  const user: User = {
    firstName: 'John'
  } as any
  
  expect(createEmailText(user)).toContain(user.firstName)
}
Copy the code
  • How should

If you need to mock test data, move the mock logic next to the object you want to mock and make it reusable.

interface User {
  id: string
  firstName: string
  lastName: string
  email: string
}

class MockUser implements User {
  id = 'id'
  firstName = 'John'
  lastName = 'Doe'
  email = '[email protected]'
}

test('createEmailText returns text that greats the user by first name', () => {
  const user = new MockUser()

  expect(createEmailText(user)).toContain(user.firstName)
}
Copy the code
  • Why do you have this bad habit

When writing tests for code that is not yet ready for extensive test coverage, there are often complex big data structures, but only parts of them are needed for the specific functionality to be tested. You don’t have to worry about other attributes in the short term.

  • Why shouldn’t you do that

In some cases, the code under test relies on attributes that we previously considered unimportant, and then needs to update all tests for that functionality.

6. Optional properties

  • What does this habit look like

Mark properties as optional, even if they sometimes don’t exist.

interface Product { id: string type: 'digital' | 'physical' weightInKg? : number sizeInMb? : number }Copy the code
  • How should

Determine which combinations exist and which do not.

interface Product {
  id: string
  type: 'digital' | 'physical'
}

interface DigitalProduct extends Product {
  type: 'digital'
  sizeInMb: number
}

interface PhysicalProduct extends Product {
  type: 'physical'
  weightInKg: number
}
Copy the code
  • Why do you have this bad habit

Marking attributes as optional rather than split types is easier and produces less code. It also requires a deeper understanding of the product being built, and may limit the use of code if changes are made to the design of the product.

  • Why shouldn’t you do that

The biggest benefit of the type system is that you can replace runtime checking with compile-time checking. With more explicit types, compile-time checks can be made for errors that might go unnoticed, such as ensuring that each DigitalProduct has a sizeInMb.

7. Use one letter

  • What does this habit look like

Name a generic with a letter

function head<T> (arr: T[]): T | undefined {
  return arr[0]
}
Copy the code
  • How should

Provide full descriptive type names.

function head<Element> (arr: Element[]): Element | undefined {
  return arr[0]
}
Copy the code
  • Why do you have this bad habit

This notation originated in the C++ paradigm library, and even the official TS documentation uses a one-letter name. It is also faster to type, simply tapping the letter T instead of writing the full name.

  • Why shouldn’t you do that

A generic type variable is also a variable, just like any other variable. By the time the IDE started showing us the type details of variables, we had slowly given up on the idea of describing them by their names. For example, we now write code using constName =’Daniel’ instead of conststrName =’Daniel’. Similarly, one-letter variable names are often confusing because they are hard to understand without reading the declaration.

8. Perform a Boolean check on values that are not Boolean types

  • What does this habit look like

Check that a value is defined by passing it directly to the if statement.

function createNewMessagesResponse (countOfNewMessages? : number) { if (countOfNewMessages) { return `You have ${countOfNewMessages} new messages` } return 'Error: Could not retrieve number of new messages' }Copy the code
  • How should

A clear review of our concerns.

function createNewMessagesResponse (countOfNewMessages? : number) { if (countOfNewMessages ! == undefined) { return `You have ${countOfNewMessages} new messages` } return 'Error: Could not retrieve number of new messages' }Copy the code
  • Why do you have this bad habit

Writing short detection code looks much cleaner and allows us to avoid thinking about what we actually want to detect.

  • Why shouldn’t you do that

Maybe we should think about what we’re actually checking. For example, the above example handles countOfNewMessages 0 differently.

9. The “lollipop” operator

  • What does this habit look like

Converts a non-Boolean value to a Boolean value.

function createNewMessagesResponse (countOfNewMessages? : number) { if (!! countOfNewMessages) { return `You have ${countOfNewMessages} new messages` } return 'Error: Could not retrieve number of new messages' }Copy the code
  • How should

A clear review of our concerns.

function createNewMessagesResponse (countOfNewMessages? : number) { if (countOfNewMessages ! == undefined) { return `You have ${countOfNewMessages} new messages` } return 'Error: Could not retrieve number of new messages' }Copy the code
  • Why do you have this bad habit

For some, understanding!! It’s like a rite of passage into the world of JavaScript. It looks short and concise, and if you’re used to it, you’ll know what it means. This is a convenient way to convert arbitrary values to Booleans. This is especially true if there are no clear semantic boundaries between virtual values, such as null, undefined, and ‘.

  • Why shouldn’t you do that

Like many coding shortcuts, use!! It’s actually confusing the actual meaning of the code. This makes the code difficult for new developers to understand, both for developers in general and for JavaScript beginners. It’s also easy to introduce subtle errors. CountOfNewMessages 0 problem in use when Boolean checking for “non-Boolean values”!! Will still exist.

10. != null

  • What does this habit look like

The lollipop operator’s little brother! = null enables us to check null and undefined at the same time.

function createNewMessagesResponse (countOfNewMessages? : number) { if (countOfNewMessages ! = null) { return `You have ${countOfNewMessages} new messages` } return 'Error: Could not retrieve number of new messages' }Copy the code
  • How should

A clear review of our concerns.

function createNewMessagesResponse (countOfNewMessages? : number) { if (countOfNewMessages ! == undefined) { return `You have ${countOfNewMessages} new messages` } return 'Error: Could not retrieve number of new messages' }Copy the code
  • Why do you have this bad habit

If your code has no discernible difference between null and undefined, then! = NULL helps simplify checking for both possibilities.

  • Why shouldn’t you do that

Although NULL was troublesome in JavaScript’s early days, it can be a valuable tool in TypeScript when it is in strict mode. A common pattern is to define null as something that doesn’t exist and undefined as something unknown, for example user.firstName=== NULL might mean that the user doesn’t actually have a name, User.firstname === undefined just means we haven’t asked the user yet.

Click to follow, the first time to learn about Huawei cloud fresh technology ~