TypesScript

This article is not a TS tutorial. It does not cover the installation, configuration, basic types, and advanced knowledge of TS. The purpose of this article is to document the cognitive changes in learning and using typescript.

I met

When I was working after graduation in 19, I knew js, which I had only read react documents but had not used in actual projects. I started my first project with nervousness and a little expectation. React + Redskull + Babel + Ant Design was selected as the technical stack of the project.

  • Dynamic typing, code refactoring crematorium, in order to avoid low-level errors in code and non-best practice writing, I useeslintStatic type checking was added to the code;
  • There are a thousand Hamlets for a thousand readers, a thousand code styles for a thousand programmers, and to keep the code style consistent, I useprettierAdd style checking to code;
  • In order to verify the correctness of the program, I added test cases to the code, usingavaTo run tests;

After adding these three axe, it is amazing. The effect is amazing. Finally, I do not feel like writing magic, and everything becomes under control.

But I was faced with a new problem:

  1. Lack of intelligent prompts, such as import, stereotype methods and properties, component props
  2. Careless spelling mistakes, likeMath.random()The method name is randoms
  3. Cannot read property 'xxx' of null/undefinedType error, etc…

The POPULARITY of TS in the front circle article in 2019 is just like the popularity of storm in the Hearthstone video barrage, which makes people feel humorous and full of sense of existence at the same time. At that time, we were sharing typescript in the group. I would like to take this opportunity to learn TS and use it in the project. Like most of my classmates, I learned TS through the official DOCUMENT of TS. After 5 minutes of reading the beginning and basic type, I was lost in thought and puzzled. I felt ts was esNext + Type, why did all my classmates who had used it find it very sweet?

I was like,

  1. Because when writing code to add the type, so ts can give when using smart tips, if you want to have prompt, will have to write type, if you want to be anywhere in the code are smart tips, will have to again and again start to end to write many types, write the type of an extra will increase your coding, spend more time, don’t let time this abundant development link
  2. I don’t know what kind of way to manage TS types. I’m worried that as the project continues to iterate, arbitrary type writing and storage will make the project more difficult to read and maintain, resulting in a lot of “type garbage”.
  3. Will there be strange potholes in redskull project with TS

The project will not be retrofitted with TS after evaluating the costs and rewards.

use

In 2020, I was lucky enough to receive a small project. Without the burden of historical technology stack, and without the fear of large project type management, I made up my mind to implement TS in the project.

In retrospect, a few interfaces were defined, using primitive types, optional attributes, and generics; Examples of official documentation for type reference in the DVA model; Type error type error type error type error type error type error type Third-party libraries that lack a TS declaration file are declared with the Declare Module. Maybe because it is a small project, it does not give play to the advantages of TS, and the project is boring.

const generateRandomNumber = (a: number, b: number) = >
  // Property 'randoms' does not exist on type 'Math'. Did you mean 'random'? ts(2551)
  Math.floor(Math.randoms() * (b - a + 1) + a);
Copy the code

But the coding experience was much better with TS. Smart tips helped me open DevDocs much less frequently, spell checkers helped me avoid potential risks, and most importantly, they alerted me to null and undefined values that I might otherwise have overlooked, allowing me to kill them at development time. In addition, I am still worried that the type of complex system is too redundant and difficult to manage. For example, the attribute of interface is mandatory in scenario A, optional in scenario B, and only part of the attribute in scenario C. All these changes will cause the type to be rewritten.

import React, { useEffect } from 'react';

const Test: React.FC = () = > {
  useEffect(() = > {
    // Object is possibly 'null'
    document.getElementById('test').scrollIntoView(); } []);return <h1 id="test">hello world</h1>;
};
Copy the code

Continue to learn

On subsequent projects, I continued to use typescript as a development language and continued to learn and be curious. I started to learn the meaning of every configuration item in tsconfig.json and began to wonder how TS found d.ts type files and gave hints when we introduced third-party modules or @types/ XXX files written in TS. Start learning ts advanced types, tool types, and some keywords. Advanced types and utility types had a huge impact on me, and it completely changed the way I thought about TS. It made me think that the power of TS was in programming types. By writing code to manipulate types, you could abstract from one type to another, through abstract operations, write very complex type definitions. Achieve amazing type inference effect.

For example, index type index-type:

/ / array
const xs = ['1'.'2'.3];
let item = xs[0]; / / "1"
type it = typeof xs[number]; // string | number

/ / object
const obj = {
  name: 'wfk'.age: 18};let name = obj.name; // "wfk"
let age = obj.age; / / 18
type nt = typeof obj.name; // string
type at = typeof obj['age']; // number
Copy the code

For example, map-type is the mapping type. [K in Keys], the new type is used to iterate over all the attributes in the old type. Keys are generally string, number, and union type. In this example, we use the keyof keyword to obtain the union typeof the attributes in the interface: keyof typeof obj

const obj = {
  name: 'wfk'.age: 18};type union = 'address' | 'phone';
type u = {
  [key in union]: string;
};
type o = {
  [key in keyof typeof obj]: string;
};
// type u = {
// address: string;
// phone: string;
// };
// type o = {
// name: string;
// age: string;
// };
Copy the code

In addition to the advanced types, there are a number of types tools built into lib.es5.d.ts that make it much easier to manipulate types. If you understand what they mean, they’re much easier to write. The built-in utility types, such as Partial, which makes all attributes in the generic T optional, and Required, which makes all attributes mandatory, use concepts from advanced types to a greater or lesser extent.

type Partial<T> = {
  [P inkeyof T]? : T[P]; };type Required<T> = {
  [P inkeyof T]-? : T[P]; };Copy the code

Can’t do without type metaprogramming

After understanding ts advanced types and tool types, I gradually cannot leave the ability of ts type metaprogramming. Looking back at the previous JS code, I will be very uncomfortable to use some third-party libraries that are not written by TS or do not provide types. When writing a type, the first reaction is whether this type can be generated by the existing value or type. Rather than just hard-coding it like a fool, and using ts’s type metaprogramming capabilities to do some really interesting type derivation.

Arrays derive union types

Defines an array, is derived through existing array definition joint types: ‘new’ | ‘second-hand’ | ‘rent’

const businessList = ['new'.'second-hand'.'rent'];
Copy the code

Using index types and value types, we can write the following type inference:

const businessList = ['new'.'second-hand'.'rent'] as const; // Note that as const is critical, otherwise we get the type string
type businessUnion = typeof businessList[number]; / / 'new' | 'second-hand' | 'rent'
Copy the code

But if businessList structure is a bit complicated, need is derived according to the following structure ‘new’ | ‘second-hand’ | ‘rent’ and ‘new’ | ‘second’ | ‘rent’ this type of joint to do?

const businessList = [
  { label: 'new'.value: 'new' },
  { label: 'second-hand'.value: 'second' },
  { label: 'rent'.value: 'rent'},];Copy the code

Using index types, value types, and utility types Pick, we can write the following type inferences:

const businessList = [
  { label: 'new'.value: 'new' },
  { label: 'second-hand'.value: 'second' },
  { label: 'rent'.value: 'rent'},]as const;
type L = Pick<typeof businessList[number].'label'>;
type V = Pick<typeof businessList[number].'value'>;
type LUnion = L[keyof L]; / / 'new' | 'second-hand' | 'rent'
type VUnion = V[keyof V]; // 'new' | 'second' | 'rent'
Copy the code

Simple implementation of object. assign

When extending object.assign (), the TS type will be lost for the same attribute name of different objects.

Assign (); assign(); assign(); assign(); assign();

const o1 = {
  a: 1.b: 1.c: 1.e: {
    name: 'wfk'.age: 18,}};const o2 = {
  a: 'b'.b: undefined.d: [1].e: {
    age: '18',}};const o3 = Object.assign({}, o1, o2);
/ / o3 results
/ / {
// a: 'b',
// b: undefined,
// c: 1,
// d: [1],
// e: {
// age: '18',
/ /},
// }
Copy the code

But we’ll see that o1 and O2 have different types of basic datatypes, of type never in O3, and different types of reference datatypes in o1 and O2, of type & in O3, which are intersections of the two, and only the types that occur in O1 or O2 get the correct type, There is even an error warning when assigning to o1.a:

o3.a = 1; // Type '1' is not assignable to type 'never'
o3.a = '1'; // Type '"1"' is not assignable to type 'never'.
Copy the code

Assign Merge field type = typescript Object. Assign merge field type = typescript Object. Assign merge field type = typescript Object. Object.assign() does lose the type of the same attribute name for different objects after merge, but after TS2.8 you can use the Spread

operator to assign(), but you can only get approximate results because:
,r>

  • Object.assign()Only enumerable ones will be copiedownpropertiesThere is no way for the type system to filter it
  • Extension operators andObject.assign()formerge({ a: 42}, {a: undefined})If the value is undefined, you get an error type{a: number}, and the result of the extension operator and assign is{a: undefined}
  • Expected results are inconsistent at compile time and run time

Object.assign is a simple implementation for double objects. We can use cross types, conditional types, map types, and index types to write type inferences like the following:

type AssignInterface<A, B> = {
  [P in keyof (A & B)]: P extends keyof B
    ? B[P]
    : P extends keyof A
    ? A[P]
    : never;
};
Copy the code

For cases like {a: 42}, {a: undefined}, the correct type {a: undefined} is obtained compared to the extended operator. It is also very simple to use, just modify the ABOVE O3 to get the correct type

const o3: AssignInterface<typeof o1, typeof o2> = Object.assign({}, o1, o2);
Copy the code

Extensions to SLATE’s built-in types

Recently, while writing a rich text editor with SLATE, there was a scenario where you needed to extend SLATE’s built-in types. The interface of SLATE Text is defined as follows

interface Text {
  text: string;
  [key: string]: unknown;
}
Copy the code

Color and backgroundColor are strings. Other extended attributes are Boolean:

interface TextExt {
  text: string;
  // Extend the attribute start
  bold: boolean;
  italic: boolean;
  underline: boolean;
  strikethrough: boolean;
  code: boolean;
  color: string;
  backgroundColor: string;
  // Extend the attribute end
  [key: string]: unknown;
}
Copy the code

We have the following array type definitions:

const TextField = [
  'bold'.'italic'.'underline'.'strikethrough'.'code'.'color'.'backgroundColor',]as const;
Copy the code

That is, how the Text provided by SLATE and the array of textfields defined get TextExt Interface as shown above. We can use Record and Omit from index types, cross types, and tool types to write the following type inferences:

type TextFieldUnion = typeof TextField[number];
type TextExt = Record<'color' | 'backgroundColor'.string> &
  Omit<Record<TextFieldUnion, boolean>, 'color' | 'backgroundColor'> &
  Text;
Copy the code

conclusion

In addition to the advantages of the above intelligent hints, spell checking, type error hints, there are many other advantages: components/libraries written in TS, type is the best document, hover over the type explanation and description, through the type can know the overall code design; Refactoring TS code will not be as worrying as refactoring JS code; For large projects with multiple maintenance, large code volume, and complex code structure, the type system can be of great help during compilation. The proper use of TS can improve the quality and efficiency of the project. However, because JS is more flexible, type metaprogramming can write very complex types that may be fun to write, but become too fancy to read, just like regular expressions, which are fun to write but painful to read. All in all, after writing so much here and there, I hope you can try TS in the project, maybe you will fall in love with it as I did.

Refer to the link

  • TS Official Documentation
  • Stack Overflow: typescript – merge – object – types