Recently, I found a typescript gymnastics question bank in the technical group of the company. Although TS was used before, it was only used as a pure type annotation. I also heard about the type programmability of TS, but I didn’t understand its meaning until I saw this library. This gives us a completely different understanding of TS

Of course, the question library topic used gymnastics skills, for people who write business code is difficult to use a few, on the basic syntax of TS to see the official document, I will not waste resources to repeat, this article to share a few I think in the actual writing business code may be used to write TS

Const Assertions

TypeScript 3.4 provides a const assertion.

  1. Literal types in literal expressions are not extended (that is, cannot be reassigned)
  2. Object properties read-only
  3. The array becomes a read-only tuple

For example

const arr = [1.2.3] as const;
arr.push(4);
Copy the code

At this point, the editor will report an error

Since arR is no longer a pure array, it is a tuple, and its type is changed from number[] to readonly [1, 2, 3].

const obj1 = {
  a: 1.b: 2
}
obj1.a // number

const obj2 = {
  a: 1.b: 2
}
obj2.a / / 1
Copy the code

Without const, the type of obj1.a would be number; Since obj2.a is readonly, the editor can tell you with certainty that both obj2.a’s value and type are 1

In a real project, this feature is primarily used to define immutable object variables

In addition, the use of const to enums can have special effects

enum X {
  A,
  B
}
const item = X.A
// will be compiled into
var X;
(function (X) {
    X[X["A"] = 0] = "A";
    X[X["B"] = 1] = "B";
})(X || (X = {}));
var item = X.A;
Copy the code

We add const

const enum X {
  A,
  B
}
const item = X.A
// will be compiled into
var item = 0 /* A */;
Copy the code

After the enum is modified to be const, the variable corresponding to the enum will be deleted from the compilation result, and the value taken from the enum will be compiled to the corresponding constant. In theory, the code size will be smaller, and the performance will be higher, after all, the absence of a variable will also reduce the process of the slave variable value

Cross type &

In actual business, it is mainly used to merge types provided by third-party libraries

For example, if your project is on the React stack and uses react-Router, the props of a page component will almost always have router-related methods and properties. It would be too cumbersome to manually add these properties to the props one by one. Fortunately, the React-Router already provides us with related types

import { RouteComponentProps } from 'react-router-dom';

function About(props: RouteComponentProps & {
  title: string;
}) {
  props.history.push('/home');
}
Copy the code

Keyof, typeof

Keyof: union type that maps a type to the names of all its members. Typeof: Type used to get variables

I mostly use these two to associate arrays with objects, for example, where the item of the array is the key of the object, or the key of the object is the item of the array

const tabList = ['home'.'about'] as const
// const tabMap = tabList.reduce((t, v) => (t[v] = '/mobile/' + v, t), {} as Record<string, string>)
// const tabMap = tabList.reduce((t, v) => (t[v] = '/mobile/' + v, t), {} as any)
Copy the code

I want the tabMap type to be {home: string; About: string}, so THAT I can write these two properties in the editor instead of looking at them myself, but in the code above I can write these two properties, Record

, any, so that I can explicitly declare the type of the tabMap:
,>

const tabMap = tabList.reduce((t, v) = > (t[v] = '/mobile/' + v, t), {} as { home: string.about: string })
Copy the code

If I change the tabList value, then I have to change the explicit typeof the tabMap as well.

const tabMap = tabList.reduce((t, v) = > (t[v] = '/mobile/' + v, t), {} as Record<(typeof tabList)[number].string>)
Copy the code

Now, no matter how the tabList value changes, the type of the tabMap automatically remains the same, right

Let’s do one more example

const vehicleTypeMap = {
  car: 1.bus: 2.plane: 3.ship: 4
}
function fn(vehicleType) {}
Copy the code

For the vehicleType parameter of the FN method, only the value of the property in vehicleTypeMap is received, that is, 1, 2, 3, 4

You might say, wouldn’t it be better to define vehicleTypeMap as an enum? In this case, of course, but in the real world, the keys of vehicleTypeMap might all have other functions, for example, the keys are vehicleTypeMap keys, and you might want to iterate over them, so you can’t define them as an enum, but you want to extract the values of those keys, So keyof, typeof

const vehicleTypeMap = {
  car: 1.bus: 2.plane: 3.ship: 4
} as const
type TVehicleType = (typeof vehicleTypeMap)[keyof typeof vehicleTypeMap] // type => 1 | 2 | 3 | 4
function fn(vehicleType: TVehicleType) {}
Copy the code

Template string type

This is a feature of Type 4.1 that can be used to narrow string-related types

For example, if you write a fetchAuth method that contains some special logic only for authentication-related interfaces whose path is prefixed with /auth, you can restrict the path of only authentication-related interfaces to be passed by controlling the path of the interface:

function fetchAuth(path: `/auth/The ${string}`) {}

fetchAuth('/auth/login') // ok
fetchAuth('/api/home') // Argument of type '"/api/home"' is not assignable to parameter of type '`/auth/${string}`'.
Copy the code

Pick

From a compound type, take a combination of several desired types

interface IApiData {
  code: number
  data: {
    name: string
    age: number
    gender: number
  }
  msg: string
}

type TData = Pick<IApiData, "data">
Copy the code

As above, fetch the type of data from IApiData

But that’s not what I’m talking about

For the above example, in most cases in a real work scenario, the desired type would be:

type Data = {
  name: string
  age: number
  gender: number
}
Copy the code

Instead of picking

the result:
,>

type Data = {
  data: {
    name: string;
    age: number;
    gender: number;
  };
}
Copy the code

So what to do? It’s really easy. You don’t have to Pick at all

type TData = IApiData["data"]
Copy the code

In my experience, this is much more widespread and common than the use of Pick, such as passing history:

import { RouteComponentProps } from 'react-router-dom';

function Child(props: { text: string; history: RouteComponentProps["history"]}) {
  // ...
}

function About(props: RouteComponentProps) {
  return <Child text="child" history={props.history}>
}
Copy the code

The generic constraint extends

The main thing is to make the type more precise by taking advantage of its ability to narrow the type range

For example, for a function fn that takes an argument that contains at least one name attribute, you might not be able to cover all scenarios if you write:

function fn(param: { name: string }) {}
Copy the code

This means that fn’s parameter param can only have a name attribute, and no more or less will do

You might think of something like this:

function fn(param: { name: string, [propName: string] :any }) {}
fn({ name: 'foo'.age: 18 }) // ok
Copy the code

But it’s more intuitive to use generic constraints:

function fn<T extends { name: string }>(param: T) {}
fn({ name: 'foo'.age: 18 }) // ok
Copy the code

For example, a function that takes an argument of type string or number returns a value of the same type as the argument. Using a union type or plain generics is not sufficient:

function fn1(value: string | number) {
  return value
}
fn1(1) // type => string | number

function fn2<T extends string | number> (value: T) {
  if (typeof value === 'number') {
    return value * 10
  }
  return value + ' world'
}
fn2(1) // type => string | number
Copy the code

This is where the generic constraint is needed:

function fn3<T extends string | number> (value: T) {
  let result: unknown
  if (typeof value === 'number') {
    result = value * 10
  } else {
    result = value + ' world'
  }
  return result as T extends string ? string : number
}
fn3(1) // type => number
fn3(' ') // type => string
Copy the code

Scenarios like this one that use ternary expressions to evaluate types (i.e., things like Y extends X? When A condition type is used in conjunction with A union type (only the union type to the left of extends), it is automatically distributed as A union type. This condition type is also known as A distributed condition type

(string | number) extends T ? A : B
/ / equivalent to
(string extends T ? A : B) | (number extends T ? A : B)
Copy the code

If you are not familiar with this feature, you may find it problematic for fn3 above:

function fn4(value: string | number) {
  fn3(value)
}
Copy the code

If the condition types without distribution features, then for the example above fn3 (value), because (string | number) extends the string? String: the result of the number is number, so fn3 (value) is the type of number, obviously this type is wrong, but actually because of distribution characteristics, fn3 (value) is of type string | number, this is in line with expectations

Distributed conditional types are also conditional, and the type to be examined (that is, the type to the left of extends) must be a naked type parameter. That is, it’s not wrapped around things like arrays, tuples, or functions

// Type distribution does not occur below
Array<string | number> extends T ? A : B
Copy the code

Naming of types

Normally, I define an interface type whose name starts with I, such as IApiData; The name of the type defined will start with T, such as TData. Some programming language has this convention, I forget which language (C#?).

In my opinion, this way of writing has two advantages

  1. Intuitively, I know at first glance that this is a type and not a variable
import { selectTypeList, verifyPathMap, CONTROL_TYPES, selectTypeItem, controlType } from 'conf'
Copy the code

I’m going to import this line, don’t click on it and see who knows which is a variable and which is a type? If replaced by:

import { selectTypeList, verifyPathMap, CONTROL_TYPES, TSelectTypeItem, IControlType } from '.. /conf'
Copy the code

That’s pretty clear

  1. The Gospel of variable naming difficulty

I have a variable called selectTypeItem, and I want to define a type for it, so I have to give it a name, so it can’t be selectTypeItem, but it has to be something that looks like it’s related to selectTypeItem, so what do I call it, Wouldn’t it be nice to add an I/T in front of selectTypeItem?

summary

In my experience, there’s no question that typescript can improve the maintainability of your project code, but you have to make sure you use it well. If it’s everywhere as any, typescript can become a liability

Finally, put in a want AD

Byte Business Products – Customer growth team is looking for outstanding front and back end students

Many commercial products, such as cross-end, BUSINESS intelligence BI, transaction realization, CRM and so on, have clear planning and sufficient funds. Now, only a programmer is needed (manual dog head).

What, you ask me whether there is really HC or whether it is someone who brushes KPI? Is it a joke to push the urgent label at the back of a list of posts?

I promise to personally follow up the whole process from the introduction of the resume into the system to the end of the interview. Please feel free to ask me about the interview progress at any time. Please send your resume to me at [email protected]