Why does it happen?any

  • I don’t know exactly how to define a type,TSError, useanyIf you can, use itany
  • I feel that defining a type is a waste of time, the project manager is pushing me, the schedule is tight,anyIt is more convenient

Frequent use ofanyThe disadvantages of

  • Not conducive to good coding habits
  • It is not conducive to subsequent maintenance of the project
  • A lot of things could have been avoidedbug

Use only when necessaryanyThe benefits of

  • Good code tips
  • Powerful static type checking
  • Readability and maintainability

So, we say no to AnyScript!

TS tends to appearanyScene combing

Add attributes to the Window global object

You see this all the time

; (<any>window).obj = {}(
  / / or
  window as any
).obj = {}
Copy the code

Doing so requires an assertion both at use and at assignment, which is cumbersome and does not prompt the code to use it

The right thing to do is

  1. In the project globalxxx.d.tsThe following code is configured in the file
interface Window {
  obj: {}}Copy the code
  1. The need to givewindowCreate a new directory below the assigned file directory@typesFolder and create a new folder in itindex.d.tsFile, add the following code
interface Window {
  obj: {}}Copy the code

Method 2 also adds the obj declaration to the global window. If the new attributes are used over a large span, it is recommended that they be placed in the project’s index.d.ts, which is easier to maintain. But method 1 can tell at a glance what attribute was added to the window in the project

Proper use of optional chain, non-null assertions

Misinterpreting typescript optional arguments and using assertions leads to pitfalls

const a: {
  b: stringc? : {d: string}} = {b: "123",}console.log((<any>a).c.d) // Error, such access will report an error, should use optional chain
console.log(a.c! .d)// Error, ts will not throw the error, but the actual access will also report an error
Copy the code

! A non-null assertion is similar to as in that it is used to assert that the value of an attribute is not null or undefined. It does not affect the final code production, but only type verification during code compilation

? . Optional chain operators affect compiled code artifacts, such as:

This ts code

const a = {
  c: undefined,}constb = a? .c? .dCopy the code

This will be compiled into the following JS code

"use strict"

var _a$c

const a = {
  c: undefined,}const b =
  a === null || a === void 0
    ? void 0
    : (_a$c = a.c) === null || _a$c === void 0
    ? void 0
    : _a$c.d
Copy the code

Correlating object property types

There are multiple properties in an object that are union types, where a is associated with b, where a is 1, b is string, a is 2, b is number and that’s how we usually define it

const obj: {
  a: 1 | 2
  b: string | number
} = {
  a: 1.b: "1.2"
}
Copy the code

When used, this results in a situation where an assertion is needed to limit the scope of B again, as shown in the code snippet below

if (obj.a === 1) {
  const [left, right] = (obj.b as string).split(".")}// If you are lazy, this may be the case again
if (obj.a === 1) {
  const [left, right] = (obj.b as any).split(".")}Copy the code

Is there any way we can not do it again? There are

const obj: {
  a: 1
  b: string
} | {
  a: 2
  b: number
} = {
  a: 1.b: "1.2"
}
// You'll notice that once you've done this, you don't need to assert again to limit the scope of obj.b
if (obj.a === 1) {
  const [left, right] = obj.b.split(".") // The check is successful
}
Copy the code

If we apply this method to function (which can also be implemented with overloading) or component argument passing, it is interesting that it can also limit the scope of argument passing. Function component implementation:

Incorrect parameter transmission. The type of A and B do not match. The verification fails

Correct parameter transmission, verification can pass

Note: You cannot deconstruct the props as this will cause the relationship between the two to be lost

const { a, b } = props // Error, the type relationship between a and B is missing
Copy the code

The use of union types needs to be treated dialectically, and it can be a bit bloated to define them in this way all the time

Smart type protection to avoid assertions

In typescript, typeof, instanceof, and in keywords are commonly used for type protection. These keywords are easy to master. There is another keyword that typescript provides: IS (type predicate). It’s another type of protection.

Type predicates allow us to do complex type checking logic in the form of functions. The declaration of a function that uses type predicates is usually of the following form:

type X = xxxx // A certain type
function check(params) :params is X
Copy the code

Params is of type X if check returns true, but not necessarily X otherwise

Imagine a scenario where a project may run in a wechat web page or other WebView

In the wechat webpage, wechat client injects various native methods into the window object, and the way to use it is window.wx.xxxx().

In other webview, we assume that there are such a native method, and the use of its way to the window. The webviewnative. XXXX ()

In typescript projects, there are no default wx and WebViewNative properties on window objects. We can explicitly define the wx and WebViewNative properties by referring to adding properties to the window global object:

interfaceWindow { wx? : {xxxx: Function} webviewnative? : {xxxx: Function}}Copy the code

If you can’t do this, you might write the assertion as any :(window as any).wx.xxxx()

You can see in the code snippet above that I have defined both attributes as optional in order to prevent direct chain calls without judgment in subsequent maintenance (iterations)

In the wechat environment, window.wx must exist, but WebviewNative must not exist. On the contrary, in other webviews, (see the previous hypothesis) Window. webviewNative must exist

In an interface, we can’t dynamically know and define which one exists

You could write it like this

if (typeof window.wx ! = ='undefined') {
    window.wx.xxxx()
} else {
    // not in wx
}
Copy the code

However, writing such expressions directly in if is too limited, or there are many ways to determine whether it is in the wechat environment, which will lead to a variety of judgments in the project, and the benefits of type predicates come out

function checkIsWxNativeAPICanUse(win: Window) :win is { wx: Exclude<Window['wx'].undefined> } & Window {
    return typeof window.wx ! = ='undefined'
}
/ / use
if (checkIsWxNativeAPICanUse(window)) {
    window.wx.xxxx()
}
Copy the code

conclusion

Reducing the use of ANY unnecessarily is both a good ts code habit and a reflection on the quality of your code

What other TS tips do you have

Original text: yzl. Xyz/Lin / 2021/05…