1. Preface ✍ ️

  • As we all know,JavaScriptIs a very flexible programming language, which on the one hand makes it the most popular programming language, on the other hand makes its code quality is uneven, high maintenance costs, runtime errors.
  • TypeScriptIs the addition ofThe type systemtheJavaScript.Suitable for any sizeThe project.TypeScriptThe type systemTo a great extentJavaScriptFaults.
  • The type system is classified according to “type check time”, which can be divided intoDynamic typeandThe static type:
    • Dynamic typeType checking is performed at runtime, and type errors in this language often lead to runtime errors, which we are familiar withJavaScriptDynamically typed, it is an interpreted language with no compile phase.
    • The static typeThe type of each variable can be determined at compile time. In this language, typing errors often result in syntax errors. Due to theTypeScriptYou need to compile as before runningJavaScriptType checking is done at compile time, soTypeScriptBelong toThe static type.
  • Maybe beginners will think usingTypeScriptAdditional code is required to reduce development efficiency. And what they may not know is,TypeScriptEnhanced editor (IDE) features, including code completion, interface hints, jump to definition, code refactoring, which greatly improves development efficiency.TypeScriptType systems can bring greater maintainability and fewer bugs to large projects.
  • In order to improve the happiness of development, the following details will be introduced how to use it well in the projectTypeScript.

2. Practice in the project

2.1 Make good use of type annotations

  • We can get through/ * * * /The form of the comment is givenTypeScriptType mark prompt:

  • The editor has a better indication when hovering over a place where this type is used:

2.2 Make good use of type extension

  • TypeScriptThere are two ways to define a type in:interface) and type aliases (type alias). In the following example, the types defined are the same except for the syntax:

  • Both interface and type aliases can be extended:

  • Interface and type aliases are not mutually exclusive, that is, interfaces can extend type aliases, and type aliases can extend interfaces:

  • TypeScript: Interfaces vs Types details the differences between interface and type aliases, which are not covered here.
  • Interface and type aliases when to choose:
    • Used when defining a public API, such as editing a libraryinterface, so that users can easily inherit the interface;
    • When defining component properties (Props) and state (State), is recommendedtypeBecause thetypeIs more restrictive;
    • typeThe type cannot be edited twice, whileinterfaceIt can be extended at any time.

2.3 Make good use of declaration documents

  • The declaration file must be.d.tsFor the suffix. In general,TypeScriptWill parse all of the items in the project*.tsFile, therefore also contains the.d.tsClosing declaration file.

  • As long astsconfig.jsonThe configuration contained intyping.d.tsFiles, then everything else*.tsThe file can get the type definition in the declaration file.

2.3.1 Third-party statement documents

  • When inTypeScriptWhen using a third-party library in a project, we need to reference its declaration file to obtain the corresponding code completion, interface prompts and other functions.
  • For most third-party libraries, the community has already defined their declaration files for us to download and use. Generally recommended use@typesUnified management of third-party library declaration files,@typesThe way to use is very simple, directlynpmyarnInstall the corresponding declaration module. In order tolodashFor example:

2.3.2 Customizing declaration files

  • When a third party library does not provide a declaration file, we need to write our own declaration file. In order toantd-dayjs-webpack-pluginFor example, when inconfig.tsThe use ofantd-dayjs-webpack-pluginIf the editor does not find its declaration file, the following error occurs:

  • When we useyarn add @types/antd-dayjs-webpack-plugin --devThe following error occurs while trying to resolve the problem:

  • You can’t find the declaration file associated with the library (search for the declaration file you need here, if you can’t find it, you don’t have it).
  • To get around the editor error, we can take another approach it provides: add an includedeclare module 'antd-dayjs-webpack-plugin';The new declaration file. We also don’t need to add files, as mentioned earliertyping.d.tsAdd the following:

The global variable

  • When we need more than onetsUse the same in the fileTypeScriptType when common practices will be inconstant.tsDeclare the related types in the file and place themexportGo out to other peopletsfileimportUse, will undoubtedly produce a lot of tedious code.
  • As mentioned earlier, as long astsconfig.jsonThe configuration contains our custom declaration file*.d.tsDeclare that the type definitions in the file can be included in the project*.tsThe file is obtained. Therefore, we can combine multipletsFiles need to use the global type written in the declaration file, need to use that typetsFile is not requiredimportYou can use it directly.

The namespace

  • In the case of a large amount of code, to avoid variable name conflicts, functions, classes, interfaces, and so on of the same module can be placed in the namespace.

  • intsFile usage:

  • Since the official document has already introduced the relevant content of the declaration document in detail, it will not be elaborated here. Students who need it can read it by themselves.

2.4 Take advantage of new JS features supported by TypeScript

2.4.1 Optional Chaining

  • Optional Chaining ? .ES11(ES2020)New features,TypeScript 3.7This feature is supported.Optional chainThis allows us to query multi-tiered objects without the need for redundant pre-validation:

  • Otherwise, access directlyuser.info.getAge()Easy to hitUncaught TypeError: Cannot read property....
  • With the optional chain, the code above would look like:

  • Optional chainIs an operator that checks to see if a property exists and then tries to access it.TypeScriptTrying to accessuser.infoBefore, the access will be attempted firstuserOnly whenuserneithernullIs notundefinedWill continue to visit ifuserisnullorundefined, the expression returns directlyundefined.
  • Currently, optional chains support the following syntax operations:

2.4.2 Nullish coalescing Operator (COALescing Operator)

  • Null-value merge operator ??ES12(ES2021)New features,TypeScript 3.7This feature is supported. When the left-hand operand is zeronullorundefined, returns its right-hand operand, otherwise returns the left-hand operand.

  • With the logic or operator (||),||The left-hand operand is zerofalsyValue (for example,' ' 或 0) returns the right-hand operand. That is, if you use||To set default values for some variables, you may encounter unexpected behavior:

2.5 Make good use of access qualifier modifiers

  • TypeScriptThe class definition of theprivate,protected 和 publicThese three access modifiers declare member access restrictions and are checked at compile time:
    • public: a public type that can be accessed inside, outside, or within a class without any modifiers.
    • protected: protected type, accessible within a class, within a subclass, but not outside the class;
    • private: private type that can only be accessed inside the current class.
  • If no modifier is added, the default value ispublicAccess level:

  • The code above is availableTypeScript PlaygroundRun up, run inJSArea, we can see the escapedPersonClass definition, with the access-qualifier removed:

  • This means that the escaped code is inJSIt can be executed correctly in the environment without limitation. But in the editor, we can seep.nameIs marked as an error, mouse over to see the specific error message.
  • TypeScriptExtensions for stricter syntax, and LSPS and compilers to help developers inThe development environmentEarly detection and resolution of existing or alternate problems.
  • However, as the example above shows, the TS compiled JS library does not limit how the end user can use it. That is, if you useTypeScriptWrite a library, useprivate 或 protectedTo restrict member access to its users as wellTypeScriptThere will be no problem when it is used, but when it is used by usersJavaScriptBut not limited by expectations.

Clearly inspired by TypeScript, ECMAScript introduced a new class definition syntax in ES2015, and started thinking about member access restrictions. It proposed a Symbol and closure private member definition scheme, which was not well accepted. After another four years of thought, design, and discussion, a specification using # signs to define private members was released in ES2019. Chrome 74+ and Node 12+ already implement the specification for this private member definition.

  • So even thoughTypeScriptThere are theprivateAccess the qualified qualifier,#privateFieldIn still inTypeScriptHas the meaning of existence.

2.6 Use type narrowing

  • TypeScriptType narrowing is fromWide typeConverted toNarrow typeIs commonly used in scenarios where variables of union type are processed.
  • inTypeScriptThere are a number of ways to narrow the type of a variable:
    • Types of assertions
    • Type the guards
    • Double assertion

2.6.1 Type Assertion

  • Type assertions can be explicitly toldTypeScriptThe detailed type of the value. When in some scenarios, we are very sure of the type of a value, even withTypeScriptWhen the inferred type is inconsistent, we can use type assertion, which has the following syntax:

  • intsxGrammar (ReactjsxThe grammaticaltsVersion) must use the former, i.eValue as type. At the same time because<>It is recommended that you use type assertions consistently because they are likely to conflict with generic syntaxValue as typeSyntax like this.
  • whenTypeScriptWhen we are not sure what type a variable of union type isOnly properties or methods common to all types of this union type can be accessed.

  • Sometimes, however, we do need to access properties or methods specific to one of the types when we are not sure of the type, such as:

  • In the example above, getanimal.swimError will be reported when. You can use type assertion at this point, which willanimalAssertion intoFishType to resolve accessanimal.swimThe wrong question:

  • Note that type assertions can only “cheat”TypeScriptCompiler, you can’t avoid itThe runtimeError, instead of abusing type assertionsRuntime errors may result:

  • TypeScriptThe compiler trusts our assertion and is callingswim()When there is no compilation error, but becauseCatThere is noswimMethod, which causes an error at run time.
  • 🏁 : Be very careful when using type assertions, and try to avoid invoking methods after assertions or referencing deep properties to reduce unnecessary runtime errors.

2.6.2 Type guard

  • There are several types of type guards:
    • Typeof: Used for judgmentnumber.string.booleanorsymbolFour types;
    • Instanceof: Used to determine whether an instance belongs to a class
    • In: Used to determine whether a property/method belongs to an object

typeof

  • You can usetypeofImplement type narrowing andneverDo a comprehensive check for the properties of the type, as shown in the following code:

  • You can see at the endelseIn the branch, we narrow it down to zeroneverfooAssign to a display declarationneverVariable, if everything is logically correct, then this should compile. But suppose one day your colleague changes itFooThe type of:

  • However, he forgot to modify it at the same timecontrolFlowAnalysisWithNeverMethod of control flow, at this timeelseThe branchfooThe type will be narrowed down tobooleanType, causing it to fail to assign toneverType, which generates a compilation error. Through the use ofneverTo avoid the occurrence of new union types without corresponding implementations, we can ensure thatcontrolFlowAnalysisWithNeverMethods are always running outFooTo ensure code security.

instanceof

  • useinstanceofThe operator Narrows the type of the variable:

in

  • useinDo a property check:

2.6.3 Double assertion

  • When we make a type assertion for a value, we need to make sure that the type inferred by the editor overlaps with the new type. Otherwise, we cannot simply make a type assertion, as shown in the following example:

  • It is important to know that any type can be asserted asanyAnd theanyCan be asserted to any type.
  • If we still want to use that type, we can use a double assertion:

  • TypeScript 3.0A new one has been added tounknownType, it is a more secureanyA copy of. Everything can be marked as yesunknownType, butunkonwnAssignment to another type must be done after type determination and condition control, and no action can be performed before type determination and condition control.
  • The double assertion operation in our example above is less reasonable, just to illustrate the effect of double assertion.
  • 🏁 : Never use double assertion unless you absolutely have to.
  • Let’s take a look at a common usage scenario: suppose we are in aTypeScriptIn the project, one was introducedJavaScriptA library written that is provided through a separate declaration fileTypeScriptSupport, then there may be a situation:

  • method() 在 .d.tsThe declared parameter type isSomeType, but since the declaration file is not updated in time, it actually accepts another type of parameter, such asnull.

  • In this case it can be usedunknownType to implement incomingnull:

  • This will pass the compiler’s type check.

2.7 Make good use of constant enumeration

  • Constant enumeration is usedconst enumEnumeration types defined:

  • Constant enumerations differ from regular enumerations in that they are removed at compile time and cannot contain computed members (that is, constant enumerator initializers can only contain literal values and other computed enumerations).
  • The result of the above example is:

  • If a calculated member is included, an error is reported at compile time:

  • The values of a normal enumeration are not evaluated at compile time, but are kept until the execution of the program. Let’s look at the following example:

  • The result of the above example is:

  • As you can see, when we don’t need an object, but the value of the object, we can use constant enumerations to avoid generating redundant code and indirect references at compile time.

2.8 Use advanced types

  • In addition tostring,number,booleanIn addition to these basic types, we should also look at some advanced uses of type declarations.

2.8.1 Type Index (KEYOF)

  • keyofSimilar to theObject.keysTo get the union type of the Key in an interface:

2.8.2 Type Constraints (extends)

  • TypeScriptIn theextendsKeywords are different from inClassAfter usingextendsThe inheritance function of generics, commonly used within generics, is to constrain generics:

  • extendsOften withkeyofUse together, for example we have onegetValueMethod is used to retrieve the value of an object, but the object is undefinedextends 和 keyofTo constrain:

  • When passed the object does notkey“, the editor will report an error.

2.8.3 Type Mapping (in)

  • inThe main function of keywords is to do type mapping, traversing the existing interfacekeyOr iterate over the union type. With the built-in generic interfaceReadonlyFor example, its implementation is as follows:

  • It makes all attributes of the interface read-only:

2.8.3 Condition Type (U? X, Y)

  • The syntax of conditional types is the same as that of ternary expressions, and is often used in cases where the type is uncertain:

  • That means ifTUA subset of alpha is a typeXOtherwise, it is a typeY. With the built-in generic interfaceExtractFor example, its implementation is as follows:

  • TypeScriptWill use theneverType to represent a state that should not exist. If the type in T exists in U, return, otherwise discard.
  • Let’s say we have two classes that have three common attributes that can passExtractExtract these three common attributes:

2.8.4 Tool Templates

  • TypesScriptThere are a number of tool generics built in, as described earlierReadonly,ExtractIn both cases, the built-in generics are inTypeScriptThe built-inlib.es5.d.tsIs defined, so it can be used directly without any dependencies.

  • Because the source code can be directly inlib.es5.d.tsAs you can see in the file, the functions and usage of several common tool templates are described below.
  • ExcludeThe function of theExtractOn the contrary, ifTThe type ofUIf it does not exist, return, otherwise discard.

  • PartialUse to set all attributes of an interface to an optional state:

  • RequiredThe effect is exactly the same asPartialInstead, change all optional attributes in the interface to required ones:

  • PickIt is mainly used to extract some attributes of the interface:

  • OmitThe reverse of Pick is used to remove some attributes of an interface:

3. Summarize 📝

  • TypeScriptVery powerful, it enhances the editor (IDE) functionality, provides code completion, interface hints, jump to definition, code refactoring, and more.
  • TypeScriptYou can andJavaScriptCoexistence, that meansJavaScriptProjects can be progressively migrated toTypeScript.
  • This article introducesTypeScriptThere are several common practices in projects that I hope I haven’t touched on yetTypeScriptOr toTypeScriptIf you are not familiar with the project, try to improve the maintainability of the code and the happiness of development 💪.

4. Reference documents

  1. TypeScript official documentation
  2. Sharp ES6 — Class grammar sugar
  3. Class element – MDN
  4. Understand TypeScript type narrowing