Recently, I have been working hard on TypeScript+React project. I have learned a lot of knowledge points from other existing projects, so I have summarized some knowledge points and provided a lot of convenience for development.

Non-null assertion operator!

Used to assert that the operation object is non-null and non-undefined. Specifically, x! Will exclude null and undefined from the x value field. Simply ignore null and undefined.

// Assertion operations are non-null and non-undefined
type ListNode = {
    data: number next? : ListNode }// Used to add the next node
function AddNext(node: ListNode) {
    if (node.next === undefined) node.next = { data: 0}}// Set the value of the next node
function SetNextValue(node: ListNode, value: number) { AddNext(node) node.next! .data = value// Declare node.next is not undefined
}

let list: ListNode = { data: 1 }
SetNextValue(list, 2)
console.log('list', list) // list { data: 1, next: { data: 2 } }
Copy the code
// 网上常见案例 忽略null和undefined
function myFunc(maybeString: string | undefined | null) {
    / / Type 'string | null | undefined' is not assignable to Type 'string'. (can't Type "string | null | undefined" assigned to Type "string".)
    // undefined is not assignable to Type 'string'.
    const onlyString: string = maybeString // Error
    const ignoreUndefinedAndNull: string = maybeString! // Ok
}
Copy the code
// Ignore undefined when calling a function
type NumGenerator = () = > number
function myFunc(numGenerator: NumGenerator | undefined) {
    // Object is possibly 'undefined'.
    // Cannot invoke an object which is possibly 'undefined'.
    const num1 = numGenerator() // Error
    constnum2 = numGenerator! (a)//OK
}
Copy the code

Non-empty assertion operators are removed from compiled JavaScript code, so be careful when you actually use them! Non-empty assertions are inherently unsafe, subjective errors, and from a defensive programming point of view, it’s not recommended to use non-empty assertions because you use them, so you don’t get an error at compile time, but you get an error at run time.

Optional chain operators? .

Returns properties or methods of the object that are not null or undefined (advantage: saves code to determine if the object is empty)

interface Foo {
    [key: string]: {
        baz: () = > void}}let foo: Foo = {
    bar: {
        baz() {
            console.log('this is function baz')}}}// Convert to JavaScript
// let newFoo = (foo === null || foo === undefined) ? undefined : foo.bar.baz()
letnewFoo = foo? .bar.baz()Copy the code

1. If foo.bar is null or undefined, access will still fail. .bar? .baz() 2. JavaScript && operator false (”, 0, NaN, false, undefined, null)? If the value is false (NaN, false, undefined, null), empty string and 0 have data 3. Can be used with arrays (eg. Arr? . [0]) method (eg. Log? .(the log method prints the description if it exists)

Null-value merge operator??

Variables are null or undefined. The following defaults

let foo: null = null
let bar = (): string= > {
    return 'this is function bar'
}
// Convert to JavaScript
// let newFoo = (foo ! == null || foo ! == undefined) ? foo : bar()
let newFoo = foo ?? bar()
console.log('newFoo', newFoo) // newFoo this is function bar
Copy the code

?? The difference between and | |

// When localstorage. volume is set to 0, the page is set to 0.5
let volume = localStorage.volume || 0.5
// When localstorage. volume is set to 0, the page is set to 0
let volume = localStorage.volume ?? 0.5
Copy the code

?? Directly with && and | | operator when used as inappropriate This kind of situation will be thrown SyntaxError

/ / can not be used in the case of not use parentheses "| |" and "??" Operation.
null || undefined ?? 'foo' // Error
// Can't mix "&&" and "??" without parentheses Operation.
true && undefined ?? 'foo' // Error

// Change to
(null || undefined)??'foo'
Copy the code

With optional chain operators? The relationship.

Null-value merge operator for null and undefined, optional chain operator? The same is true. The optional chain operator is useful for accessing objects whose attributes may be NULL or undefined

interface Customer {
    name: string city? : string }let customer: Customer = {
    name: 'xiaoming'
}
letcustomerCity = customer? .city ??'Unknown city'
Copy the code

Private field #XXX

class Person {
    // Special identifiers are only available for ECMAScript 2015 and later.
    #name: string;
    constructor(name: string) {
        this.#name = name;
    }
    greet() {
        console.log(`Hello, my name is The ${this.#name}! `); }}let semlinker = new Person("Semlinker");
semlinker.#name
Copy the code

Cross type &

Combine multiple types into a single type

interface Button {
    type: string
    text: string
}

interface Link {
    alt: string
    href: string
}

const BottonLink: Button & Link = {
    type: 'danger'.text: 'Jump to Baidu'.alt: 'Jump to Baidu'.href: 'http://www.baidu.com'
}
Copy the code

Joint type |

Indicates that its type is connected to any one of multiple types

interface Button {
    type: 'default' | 'primary' | 'danger'
    text: string
}

const btn: Button = {
    type: 'danger'.text: 'button'
}
Copy the code

Condition types (U? X: Y) the same syntax rules as ternary expressions

Type index keyof

Key gets the associative type of the key in an interface like Object.key

interface Button {
    type: string
    text: string
}

type ButtonKeys = keyof Button
/ / equivalent type ButtonKeys = 'type' | 'text'
Copy the code

Type constraint extends

In class, it inherits and in generics, it constrains generics

Tip: If you want to use a class interface, you need to implement the interface with implements. In addition, an interface can also use an interface, and extends the interface.

type BaseType = string | number | boolean

// The range of the generic T is BaseType. Arg parameter value types can only be BaseType
function Copy<T extends BaseType> (arg: T) {
    return arg
}
Copy('this is function Copy')
Copy the code
// extends is often used with keyof to get the value of an object but the object is uncertain
const list = {
    name: 'xiaoming'.age: 18
}

function getValue<T.P extends keyof T> (obj: T, key: P) :T[P] {
    return obj[key]
}

console.log('getValue', getValue(list, 'name')) // getValue xiaoming
Copy the code

Type mapping in

Mapping of main types, traversing keys of existing interfaces or traversing cross types

Tool type Readonly Example
type Readonly<T> = {
    / / keyof T get a joint types' name '|' age
    / / P keyof equivalent to perform in a forEach logic traversing the 'name' | 'age
    readonly [P in keyof T]: T[P]
}
interface obj {
    name: string
    age: string
}
type ReadOnlyObj = Readonly<obj> // Name age in obj is read-only
Copy the code

Partial

Properties defined in an interface type become optional

// Source code implementation
type Partial<T> = {
    [P inkeyof T]? : T[P]; };Copy the code

Required

Properties defined in an interface type become mandatory

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

Through the -? Remove? From optional attributes Makes a property optional to mandatory

Readonly

Properties defined in an interface type become read-only

// Source code implementation
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};
Copy the code

Record<K extends keyof any, T>

The type that takes two type variables Record generates has property values that exist in type K extends Keyof any extends Keyof any what is keyof any? Is type K was roughly constraints in string | number | symbol just object index types, namely type K can only be specified for the several types, according to the following scenario can be better understood

Scene 1: Constructing an object array frequently in business code But the array is not convenient to index So we will sometimes bring out a field of an object as an index to construct a new object Suppose you have an array of goods list To be found in the list of goods commodities called xx We tend to iterate through way set in finding more cumbersome In order to facilitate We’re going to rewrite this array as an object

interface Goods {
    id: number
    name: string
    price: number
    url: string
}

const goodsMap: Record<string, Goods> = {}
const goodsList: Goods[] = [{
    id: 1.name: 'xiaoming'.price: 100.url: 'www.baidu.com'
}]

goodsList.forEach(item= > goodsMap[item.name] = item)
console.log('goodsMap', goodsMap) 
// xiaoming: { id: 1, name: 'xiaoming', price: 100, url: 'www.baidu.com' }
Copy the code

Scenario 2: Used for interface Response type declaration

type Car = 'Audi' | 'BMW' | 'MercedesBenz'
type CarList = Record<Car, { price: number }>

const car: CarList = {
    'Audi': { price: 20 },
    'BMW': { price: 30 },
    'MercedesBenz': { price: 40}}console.log('car', car)
// car {
// Audi: { price: 20 },
// BMW: { price: 30 },
// MercedesBenz: { price: 40 }
/ /}
Copy the code

Pick<T, K extends keyof T>

The generic T checks out the specified properties and forms a new object type scenario: Todo(preview is just the finished state of the title required) Simple understanding of extracting the desired properties from an object

interface Todo {
    title: string
    completed: boolean
    description: string
}

type TodoPreview = Partial<Pick<Todo, 'title' | 'completed'>>
const todo: TodoPreview = {
    title: 'clean room',}Copy the code

Exclude<T, U>

Property to exclude types from the generic T that can be assigned to the generic U

type num = 1 | 2 | 3
type numExclude = Exclude<num, 1 | 2>
const numList: numExclude = 3
Copy the code
interface Worker {
    name: string
    age: number
    email: string
    salary: number
}

interface Student {
    name: string
    age: number
    email: string
    grade: number
}

type ExcludeKeys = Exclude<keyof Worker, keyof Student>
// Select * from Student where Worker salary does not exist
let ExcludeKeysList: ExcludeKeys = 'salary'
Copy the code

In contrast to Pick, Pick is used to Pick out attributes we need to care about, and Exclude is used to Exclude attributes we don't need to care about. Exclude is rarely used on its own and can be combined with other types to achieve more complex and useful functions

Extract<T, U>

Extract a type from the generic T that can be assigned to the generic U

type num = 1 | 2 | 3
type numExclude = Extract<num, 1 | 2>
const numList: numExclude = 1 // 1 or 2
Copy the code

Omit<T, K extends keyof any>

Extract the attribute types from the generic T that are not in the generic K and form a new object type

interface IUser {
    name: string
    age: number
    address: string
}

type IUserOmit = Omit<IUser, 'name' | 'age'>
// Ignore the name and age attributes in the IUser interface
let iUserOmit: IUserOmit = {
    address: 'shenzhen'
}
console.log('iUserOmit', iUserOmit) // iUserOmit { address: 'shenzhen' }
Copy the code

NonNullable

Exclude null and undefined from the generic T

type U = NonNullable<'name' | undefined | null>;
let text: U = 'name'
Copy the code

Parameters

any> ConstructorParameters

any> ReturnType

any> InstanceType

any> and other tool types. Tool types greatly improve the scalability of types and facilitate reuse. If there is any problem above, please correct it and modify it in time. There are more useful methods, please give more advice!