preface

To get ready for the release of vUE 3.0, learn a new typescript stream before the volume of code in the repository starts to swell. I first learned typescript when I first started working on the front end in 2017, just reading a few blogs about it. I learned typescript for the second time in 2018, the second year of my work. My understanding and use of TS was limited to basic functions such as type checking, and I had no idea about some advanced concepts in TS. This study is to prepare for a systematic study and summary of TS. By the way, some of the typescript Chinese documentation is already out of date. For those of you who are good at English, read the TS website. For outdated content on TS Chinese, I use the emoji”👻” to mark it. This article is a note I took when I was learning TS.

The base type

/ / a Boolean
const isCheck: boolean = false
/ / digital
const year: number = 2077
/ / string
const name: string = 'Cyberpunk'
/ / array
const list: number[] = [1.2.3]
const list: Array<number> = [1.2.3]
Copy the code

Tuple

The Tuple represents an array of known elements and types. Ts throws an error when accessing a crossover element.

👻 : In typescript’s Chinese language, access cross-boundary elements and use federated types. It’s outdated content.

const tuple: [number.string] = [2077.'Cyberpunk']
Copy the code

Enum

You can provide more semantic names for a set of data

enum language {CPP = 1, JS = 2, JAVA = 3}
const lan: language = language.CPP / / 1
// Enumeration type. You can also obtain the enumeration name from the enumeration value
const lanName: string = language[2] / / 2
Copy the code

Any

Any specifies a type for variables whose type is unknown, and variables will not be typed.

Void

Void means there is no type. If a variable is of type Void, only undefined or null can be assigned.

Null & Undefined

Null, undefined is a subtype of any type. Null and undefined can be assigned to number or string.

If the tsconfig.json configuration item strictNullChecks is set to true, then null, undefined can only be assigned to their respective types and Void types.

Never

Never indicates a type that Never exists. For example, a function always throws an error and returns no value. Or a function that has an infinite loop inside that never returns a value. The return value of this function is of type Never.

Object

Object represents a non-primitive type, that is, a type other than number, string, Boolean, symbol, NULL, or undefined. For example, if a function takes an object as a parameter and the key and value of the parameter are indeterminate, the parameter can be of type Object.

Types of assertions

Type assertions tell the compiler that we know exactly what type a variable is, without type checking.

// as syntax or Angle bracket syntax
alert((temp as string).length)
alert((<string>temp).length)
Copy the code

Double assertion

S as T, the assertion can only succeed if S is a subset of T or T is a subset of S

window.onclick = function (event: Event) {
  let mouseEvent = event as HTMLElement // Error, Event, HTMLElement types are not parent-child sets
  mouseEvent = (event as any) as HTMLElement // ok
}
Copy the code

interface

The main purpose of an interface is to name types. The type checks the structure of the value. In TS you don’t say, “This object implements this interface.” You only care about the shape of the value, and as long as the object satisfies the shape of the value of the interface, then it is allowed.


interface Bar { name: string }
interface Foo { name: string }

const bar: Bar = { name: 'Geralt' }
const foo: Foo = { name: 'V' }

function getName (hero: Bar) { return hero.name }
// Hero accepts the Bar type, but Foo and Bar are identical in structure, so using Foo is allowed
getName(bar) // Geralt
getName(foo) // V
Copy the code

Optional attribute

Is the optional attribute added after the attribute defined by the interface? , indicating that some properties in an object exist only under certain conditions or not at all.

Read-only property

Read-only attributes add the readonly keyword to the attributes defined by the interface. Once a read-only property is created, it cannot be modified.

A read-only array

Use the ReadonlyArray keyword to declare a read-only array. But we can bypass the compiler by modifying read-only arrays with type assertions.


let list: ReadonlyArray<number> = [1.2.3]
// error
list.push(4)
// ok
(list as number[]).push(4)
Copy the code

Additional property checking

Object literals undergo additional property checks, and the compiler throws an error if the object literals have properties that do not exist in the target type.


interface Bar { name: string, age? :number }
function getName (hero: Bar) { return hero.name }

// error, object literals perform additional property checks
getName({ name: 'Geralt', gender: 'man' })
// ok, using type assertions bypasses the check
getName({ name: 'Geralt', gender: 'man' } as Bar)
// ok, use a variable that does not perform additional attribute checking
const bar = { name: 'Geralt', gender: 'man' }
getName(bar)
// Or modify the interface definition to use index signatures for unknown attributes
Copy the code

Function types

Interfaces can also describe functions. The parameter name of the function implementing the interface does not need to match the parameter name defined by the interface.

interface FooFunc {
  (a: number, b: string) :string
}
const foo: FooFunc = (a: number, b: string) :string= > a + b
// If you do not specify a type, TypeScript automatically deduces the type of the parameter and return value
const far: FooFunc = (a, b) = > a + b
Copy the code

Indexable type

Indexable types have an index signature that describes the type of the object index and the corresponding index return value type. TypeScript supports two index types, strings and numbers. Both index types can be used in code, but the type of the numeric index return value must be a subtype (subset) of the string index. Because Foo[‘1’] and Foo[1] are identical.


interface Foo {
  [key: number] :string,
  [key: string] :string
}

interface NumberDictionary {
  [index: string] :number;
  length: number;    // ok
  name: string;      // error
}
interface Foo {
  [key: string] :number | string;
  length: number;    // ok
  name: string;      // ok
}
Copy the code

The nesting of indexable types

To take a quick example, the following indexable types are not safe

interfaceFoo { color? :string
  [key: string] :string
}
// Because of a clerical error, the spelling was wrong, color -> colour, but because of indexable type, this does not throw an error
let a: Foo = {
  colour: 'blue'
}
Copy the code

The logical way to think about it is to separate indexable types into separate attributes

interfaceFoo { color? :stringother? : { [key:string] :string}}// The error compiler throws an error
let a: Foo = {
  colour: 'blue'
}
Copy the code

Class types

TypeScript uses interfaces to force instance parts of a class to implement properties or methods. But for static parts, such as static properties and methods, constructor is outside the scope of inspection.


interface WitcherClass {
  name: string; // The interface can define attributes of 'instances'
  getName(): string; // The interface can define methods of 'instances'
}

The Witcher class implements the WitcherClass interface
class Witcher implements WitcherClass {
  name: string;
  getName () { return this.name }
  constructor() {}}Copy the code

Inherited interface

Interfaces can be inherited using the extends keyword. An interface can inherit multiple interfaces at the same time to achieve a composite interface.


interface Witcher { name: string; }

interface Wolf extends Witcher { fencing: string }

const geralt: Wolf = {
  name: 'geralt'.fencing: 'New Year's Greetings with swords'
}
Copy the code

Interface inheritance class

An interface can inherit from a class, but only the definition of the class, not the implementation of the class. The interface inherits the modifiers of the private and protected members of the class. When an interface inherits from a class that contains private and protected members, the interface can only be implemented by that class and its subclasses.

class Foo { protected name: string }
interface InterfaceFoo extends Foo { getName(): string }
// ok
class Bar extends Foo implements InterfaceFoo {
  getName() { return this.name }
}
// error, Far has no name attribute, only Foo and its subclasses
class Far implements InterfaceFoo {
  getName() { return this.name }
}
Copy the code

class

The modifier

  • Public Default modifier. Members of a TypeScript class default to public
  • Members of a private class cannot be accessed outside the class, nor can subclasses
  • Members of protected classes are not accessible from outside the class, but from subclasses. If the constructor of a class is protected, the class can only be inherited, not instantiated.
  • Readonly keywordreadonlyYou can set the property of an instance to read-only

Parameter properties

Use private, protected, public, or readonly in constructor arguments to combine declaration and assignment

// Boo, Far declares the name attribute in the same way
class Boo {
  public name: string
  constructor (theName: string) {
    this.name = theName
  }
}
class Far {
  constructor (public name: string) {}}Copy the code

getters/setters

You can use getters/setters to control reading and writing to class members, but if members of a class have only GET, they are read-only by default.

class Boo {
  // control the getters/setters behavior of _name
  private _name: string
  get name(): string {
    return this._name
  }
  set name (newName): void {
    if (newName) {
      this._name = newName
    }
  }
}
Copy the code

Static properties/static methods

Use the static keyword to define a class’s static properties or methods, using the class name.

class Boo {
  static lan: string = 'JAVA' 
  static getLan () {
    return Boo.lan
  }
}
// JAVA
console.log(Boo.getLan())
Copy the code

An abstract class

Define abstract classes using the abstract keyword. Abstract classes can only be inherited, not directly instantiated. Abstract can also be used in abstract classes to define abstract methods, which do not contain concrete implementations and must be implemented in subclasses.

abstract class Boo {
  public name: string = 'boo'
  public abstract getName(): string
}
class Bar extends Boo {
  // Subclasses must implement abstract methods of the abstract class
  getName () {
    return this.name
  }
}
Copy the code

typeof

The type of the instance declared when declaring a class in TypeScript. Using typeof class names takes the typeof the class itself.

class Bar {
  static lan: string = 'Java'
  constructor (public age: number) {}}const a: Bar = new Bar(20)
// typeof class name, which retrieves the typeof the class itself, including static members and constructors
const b: typeof Bar = Bar
// Java
console.log(b.lan)
Copy the code

Private versus singleton mode

class Bar {
  private static instance: Bar
  private constructor () {}
  public static create() {
    if(! Bar.instance) { Bar.instance =new Bar()
    }
    returnBar.instance; }}let a = new Bar() // error, the constructor cannot be accessed outside the class
let b = Bar.create() // ok
Copy the code

function

Function types

// Function type
const sum = (a: number, b: number) :number= > a + b
// Full function type. Parameter names can be different
const sum: (a: number, b: number) = > number = (x: number, y: number) = > x + y
Copy the code

Inference type

// The ts compiler will automatically infer the type of x, y, and the return value of the function
const sum: (a: number, b: number) = > number = (x, y) = > x + y
Copy the code

Optional parameters, default parameters, remaining parameters

// Optional argument, use '? 'Realize optional parameter function, optional parameter must be at the end of the required parameter
constsum = (a: number, b? : number):number= > {
  if (b) {
    return a
  } else {
    return a + b
  }
}
// The default value of the parameter. If the parameter value is undefined, the default parameter will be used
const sum = (a: number, b: number = 0) :number= > a + b
// The remaining parameters, using ellipsis to define the array of remaining parameters
const sum = (.arguments: number[]) = > {
  return arguments[0] + arguments[1]}Copy the code

This parameter

This appears in the function’s object literal, which defaults to type any. You can add the this argument to a function, which is a dummy argument, first in the argument list

interface Bar {
  name: string
}
function foo () {
  return {
    name: this.name // This is of type any}}function bar (this: Bar) {
  return {
    name: this.name // This is of type Bar}}Copy the code

overloading

Function overloading can be used when we need to return different types of results based on different types of arguments. Provide multiple function type definitions for the same function for function overloading. The compiler handles function calls based on this list. The compiler looks through the list of overloads in turn to find matching function definitions.

Function foo(x: number): string function foo(x: string): number // function foo(x): any any { if (typeof x === 'number') { return x + '' } else if (x === 'string') { return Number(x) } }Copy the code

The generic

Use generics to create reusable components that can support multiple data types

// The echo function is generic
function echo<T> (arg: T) :T {
  return arg
}
// Explicitly pass in type parameters
echo<string> ('Hello')
// Using type inference for ts, the compiler automatically verifies the type of T based on the argument
echo('Hello')
Copy the code

Generic variables

For a generic parameter, we must treat it as any or all types.


function echo<T> (arg: T) :number {
  / / error. Boolean, numeric types do not have length attributes
  return arg.length
}
Copy the code

A generic interface

interface Foo {
  <T>(arg: T): T
}
const foo: Foo = <T>(arg: T): T= > arg

// Specify the generic type by treating the generic parameter as an interface parameter
interface Foo<T> {
  (arg: T): T
}
// Arg will be derived to type number
const foo: Foo<number> = (arg) = > arg
Copy the code

A generic class

Classes are divided into static parts and instance parts. Generic classes refer to the types of instance parts and cannot be used for static parts

class Foo<T, U> {
  static name:T // error, static members cannot be generic types
  constructor(public x: T, public y: U) {}
  getY(): U { return this.y }
  getX(): T { return this.x }
}
const foo = new Foo('CyberpuCk'.2077)
Copy the code

Generic constraint

interface NameConstraint {
  name: string
}
// Constrain the generic parameter T to contain the name attribute, not any type
function witcher<T extends NameConstraint> (people: T) :T {
  return people
}
// ok
witcher({ name: 'geralt' })
// error, must have the name attribute
witcher('geralt')
Copy the code

Use class types in generics

Use the new (… Any: []) => T, reference the class type

Function create<T>(c: new(... any[]) => T): T { return new c(); }Copy the code

Use generics wisely (with examples from Axios)

import axios from 'axios'

// A generic return structure
// Use generics to encapsulate a common interface for returning data
interface ResponseData<T = any> {
  code: number;
  result: T;
  message: string;
}

// Encapsulate the request function
// Request user data
function getUser<T> () {
  return axios.get<ResponseData<T>>('/user').then((res) = > {return res.data
  }).catch(error => { }} // Request a set of user datafunction getUsers<T>(a) {
  return axios.get<ResponseData<T> > ('/users').then((res) = > {return res.data
  }).catch(error => { })}Copy the code
// User interface
interface User {
  id: string,
  name: string
}
/ / use
async function test () {
  await getUser<User>()
  await getUsers<User[]>()
}
Copy the code

The enumeration

Digital enumeration

Numeric enumeration without specifying an initial value, enumeration values are incremented from 0. If a member is initialized to 1, subsequent members are incremented from 1.

String enumeration

Each member of the enumeration must be initialized with a string.

Heterogeneous enumeration

Enumeration members, numeric members, and string members mixed. But it’s best not to use it that way.

Enumerated type

When all enumerators have literal enumeration values, enumerations take on special semantics and can become a type.

enum Witcher {
  Ciri,
  Geralt
}
// ok
let witcher: Witcher = Witcher.Ciri
// error
let witcher: Witcher = 'V'
Copy the code

Runtime enumeration

At run time, an enumeration is a real object that can be used as an object

enum Witcher {
  Ciri = 'Queen',
  Geralt = 'Geralt of Rivia'
}
function getGeraltMessage(arg: {[key: string] :string}) :string {
  return arg.Geralt
}
getGeraltMessage(Witcher) // Geralt of Rivia
Copy the code

Compile-time enumeration

At run time, though, an enumeration is a real object. But using keyof doesn’t behave like a normal object. You must use keyof Typeof to get all attribute names for the enumeration.

👻 : This is not available in typescript Chinese

enum Witcher {
  Ciri = 'Queen',
  Geralt = 'Geralt of Rivia'
}
type keys = keyof Witcher / / the toString, charAt...........................
type keys = keyof typeof Witcher // Ciri, Geralt, all enumerated types
Copy the code

Const enumeration

Const enumerations are removed during TS compilation to avoid additional performance overhead.

const enum Witcher {
  Ciri = 'Queen',
  Geralt = 'Geralt of Rivia'
}
const witchers: Witcher[] = [Witcher.Ciri, Witcher.Geralt]
/ / the compiled
// const witchers = ['Queen', 'Geralt of Rivia']
Copy the code

Enumerated static methods and properties

A namespace with the same name and an enumeration type will undergo a “declaration merge”, and the functions and variables of the namespace export will become static methods of the enumeration, static properties.

enum Color { Blue, Yellow, Green }
namespace Color {
  export function print(color: Color) :void {
    alert(Color[color])
  }
  export const name = 'colors'
}
Color.print(Color.Blue) // Blue
alert(Color.name) // colors
Copy the code

Open enumeration

Enumerations with the same name will be merged, but you need to specify an initial value for the second enumeration with the same name.

enum Color { Red, Green, Blue }

enum Color { Yellow = 3, Black }
Copy the code

Type inference

// x is automatically inferred as number
let x = 2
/ / zoo is automatic inference as (Rhino | Elephant | Snake) [] type
// If Rhino, Elephant, and Snake have the same supertype of Animal, zoo will be inferred to be Animal[]
let zoo = [new Rhino(), new Elephant(), new Snake()];
Copy the code

Context type

Typescript extrapolates the type of the function on the right from the type of window.onmousedown.

👻 : This is inconsistent in typescript’s Chinese language.

// typescript can infer that mouseEvent is of type mouseEvent
window.onmousedown = function(mouseEvent) {
  // ok
  console.log(mouseEvent.button)
  // error
  console.log(mouseEvent.kangaroo)
}

window.onscroll = function(uiEvent) {
  // Error, uiEvent is automatically inferred to be of the uiEvent type. The uiEvent type does not contain the button attribute
  console.log(uiEvent.button)
}
Copy the code

noImplicitAny

Enable the compiler option noImplicitAny. The compiler will warn when type inference can only be inferred as any

Type compatibility

Typescript type compatibility is structure-based, not notional. The following code is perfectly fine in TS, but throws errors in name-based languages such as Java.

interface Named { name: string }
class Person {
  name: string
}
let p: Named
// ok
p = new Person()

// We can use const assertions directly.
let c = 'python' as const
c = 'Swift' // error
Copy the code

Covariant, Contravariant, Bivariant

  • Covariant parent = subclass 👌; Subclass = parent class 🙅
  • Contravariant parent = subclass 🙅; Subclass = parent class 👌
  • Bivariant parent = subclass 👌; Subclass = parent class 👌

Object Compatibility Covariant

interface Foo {
  name: string;
}
interface Bar extends Foo {
  age: number;
}
letx! : Foo;lety! : Bar; x = y// ok
y = x // error
Copy the code

✨ Function Compatibility

Function parameter type compatibility Bivariant Contravariant

Compatibility of function parameters, with bidirectional covariance

/ / animals
interface Animal {
  name: string
}
/ / the cat
interface Cat extends Animal {
  color: string
}
// American bobtail cat
interface USAShorthair extends Cat {
  age: number
}
type handle = (it: Cat) = > void

// Ok, it is safe
const handle1: handle = (it: Animal) = > {}

// Ok, not safe, but allowed
// Since the type handle argument is Cat, it is allowed to accept other Cat subtypes
// If it is USAShorthair, other subclasses of Cat will be rejected, so it is not secure
const handle2: handle = (it: USAShorthair) = > {}
Copy the code
Disable function parameter bidirectional covariant Contravariant

Enabling strictFunctionTypes disables bidirectional covariant of function parameters. The parameter type is contravariant

type handle = (it: Cat) = > void
// ok
const handle1: handle = (it: Animal) = > {}
// error
const handle2: handle = (it: USAShorthair) = > {}
Copy the code

Function return value type Compatibility (Covariant)

If the function returns an object, the compatibility is the same as the object

interface Foo {
  name: string;
}
interface Bar extends Foo {
  age: number;
}
// Bar is a subclass of Foo, and return value type compatibility is covariant
// Foo (parent) = Bar (subclass) ok
let x: (a)= > Foo;
let y: (a)= > Bar;
x = y; // ok
y = x; // error
Copy the code

Quantitative compatibility of function arguments

A function that allows you to ignore some arguments (contravariant?)

let x: (a: number) = > void = (a) = > {} // The number of parameters is small
let y: (a: number, b: string) = > void = (a, b) = > {} // The number of parameters is large
y = x; // ok
x = y; // error
Copy the code

Enumeration compatibility

Enumeration types are compatible with numeric types; different enumeration types are incompatible

enum Bar { T, R }
enum Foo { T, R }
// ok
const a: number = Bar.T
// error
const b: Foo = Bar.T
Copy the code

Class compatibility

The compatibility of class types is similar to that of object literals. But class types only compare instance parts of classes; static members and constructors fall outside the scope of compatibility comparisons.

class Bar {
  constructor(public name: string) {}}class Foo {
  constructor(public name: string.public age: number) {}}class Faz {
  constructor(public age: number) {}}class Boo {
  static version: number
  constructor(public name: string) {}}letbar! :Barletfoo! :Fooletfaz! :Fazletboo! :Boo foo = bar// error, missing age instance attribute, compatibility rules are similar to objects
bar = foo // ok
bar = faz // Error, name and age are incompatible
boo = bar // ok, static members do not compare compatibility
Copy the code

Generic compatibility

When generics are compared for compatibility, you need to specify generic parameters for comparison. When no generic parameter is specified, the default type is any.

type Bar<T> = {
  name: T
}
type Foo<U> = {
  name: U
}
type Far<R> = {
  name: R
}
leta! :Bar<string>letb! :Foo<string>letc! :Far<number> a = b// ok
b = c // error
Copy the code

Compatibility Summary

  • Object Compatibility Covariant
  • Function compatibility
    • Function parameter compatibility Bivariant – safer -> Contravariant
    • Function return value Compatibility (Covariant)
    • Function parameter number Contravariant
  • Enumeration compatibility (number <= enumeration OK, enumeration A <= enumeration B error)
  • Class compatibility (similar to object compatibility type)
  • Generic compatibility (need to specify generic parameters after comparison)

Nominal type

Ts compatibility is structure-based. But sometimes we do want to distinguish between two types that have the same structure.

Use literal types

Use literal types to distinguish types of the same structure

interface People<T extends string> {
  name: string,
  age: number,
  color: T
}
letblackPeople! : People<'black'>
letwhitePeople! : People<'white'>
// Same structure, but incompatible types
// The example does not involve any racist ideas
blackPeople = whitePeople // error
whitePeople = blackPeople // error
Copy the code

Use enumerated

interface Foo { name: string }
enum FooEnum {}
interface Boo { name: string }
enum BooEnum {}
type FooTitular = Foo & FooEnum
type BooTitular = Boo & BooEnum
let a:FooTitular = { name: '123' } as FooTitular
let b:BooTitular = { name: '456' } as BooTitular
// Same structure but not compatible
a = b // error
b = a // error
Copy the code

Using an interface

Breaking structural compatibility by adding an unrelated attribute to a type, the naming convention for unrelated attributes usually begins with _ and ends with Brand

interface A extends String {
  _aBrand: number;
}
interface B extends String {
  _bBrand: number;
}
let a: A = 'a' as any;
let b: B = 'b' as any;
// Same structure, but incompatible type
a = b; // error
b = a; // error
Copy the code

✨ Advanced type

Cross type

Stacking multiple types into one type

function assign<T extends object.U extends object> (x: T, y: U) :T & U {
  return{... x, ... y } }class Foo {
  constructor(public foo:string) {}}class Bar {
  constructor(public bar: string) {}}const boo = assign(new Foo('foo'), new Bar('bar')) // Boo is a crossover type of Bar & Foo
boo.bar // ok
boo.foo // ok
Copy the code

The joint type

Union types indicate that a value can be one of several types. When a value is a union type, we can be sure that it contains common members of multiple types.

leta! :Boo | Foo | Baz a.sex// Error, when a is Boo, Baz, there is no sex attribute
(a as Foo).sex // ok
Copy the code

Type of protection

Custom type protection

Define a special function that returns a predicate of type arg is type. Arg is an argument in a custom type protection function.

interface Boo { name: string }
interface Foo { sex: boolean }
interface Baz { age: number }
type BooFooBaz = Boo | Foo | Baz
leta! : Boo | Foo | Bazfunction isBoo(arg: BooFooBaz) :arg is Boo {
  return (arg asBoo).name ! = ='undefined'
}

alert(a.name) // error
if (isBoo(a)) {
  alert(a.name) // ok
}
Copy the code

Typeof type protection

Provides type protection for number, String, Boolean, and symbol. We can do without abstracting typeof into a special function that returns a type predicate.

leta! :number | string
if (typeof a === 'string') {
  alert(a.length) // ok
}
if (typeof a === 'number') {
  alert(a - 1) // ok
}
Copy the code

Instanceof protection

Provides type protection for class types

class Foo {
  getFoo(): void{}}class Bar {
  getBar(): void{}}leta! : Bar | Fooif (a instanceof Foo) {
  a.getFoo() // ok
  a.getBar() // error
}
if (a instanceof Bar) {
  a.getBar() // ok
  a.getFoo() // error
}
Copy the code

In type protection

interface A { x: number }
interface B { y: string }
function doStuff(q: A | B) {
  // Determine if there is an x attribute
  if ('x' in q) {
    alert(q.x)
  } else {
    alert(q.y)
  }
}
Copy the code

Literal-type protection

interface Foo {
  name: string
  type: 'foo'
}
interface Bar {
  name: string
  type: 'bar'
}
function temp (arg: Foo | Bar) {
  if (arg.type === 'foo') {
    / / the type Foo
  } else {
    / / Bar type}}Copy the code

Null type

Null, undefined can be assigned to any type. Adding strictNullChecks to tsconfig.js prevents this behavior. After strictNullChecks is enabled, a variable does not automatically contain null and undefined by default.

// start strictNullChecks
let a: string = '123'
a = null // ok

StrictNullChecks is enabled
let b: string = '123'
b = null // error
Copy the code

Assertion excludes undefined, null type

Add! After the variable name. You can assert that undefined and null types are excluded

let a: string | null | undefined
a.length // errora! .length// ok
Copy the code

✨ Type alias

type Foo = string
type Bar = {
  name: string,
  age: number
}
Type aliases can also be used to declare functions
type Func = {
  (a: number) :number;
  (a: string) :string;
}
// Type aliases can also be generic
type Boo<T> = {
  key: T
}
// The type alias can refer to itself
type LinkListNode<T> = {
  value: T,
  next: LinkListNode<T> | null,
  prev: LinkListNode<T> | null
}
Copy the code

The difference between type aliases and interfaces

  1. A type alias can introduce a name for any type. Examples are basic types, union types, and so on
  2. Type aliases do not support inheritance
  3. Type aliases do not create a real name
  4. Type aliases cannot be implemented, whereas interfaces can be implemented by derived classes
  5. The compiler throws an error when a type alias is the same name, and merges when an interface is the same name

String literal type

leta! :'js'
a = 'js' // ok
a = 'java' // error

letb! :'c#' | 'c++'
b = 'c#' // ok
b = '.net' // error
Copy the code

Numeric literal type

letx! :1
x = 2 // error

lety! :1 | 2
y = 2 // ok
y = 3 // error
Copy the code

The index type

Index type query operator

The result of keyof T is a union of public property names on T

type Keys<T> = keyof T
interface Witcher {
  name: string,
  age: number
}
// Witcher public attribute name union
let keys: Keys<Witcher>; // name | age
Copy the code
type Keys<T> = keyof T
type Witchers = [string.string]
let keys: Keys<Witchers>; / / '0' | '1' | length | toString...

type Keys<T> = keyof T
type Witchers = Array<string>
let keys: Keys<Witchers>; / / number | length | toString...
Copy the code
function pluck<T.K extends keyof T> (o: T, names: K[]) :T[K] []{
  return names.map(n= > o[n])
}
const witcher = {
  name: geralt,
  age: 88
}
// (string | number)[]
// [' geralt ', '88']
const values = pluck(witcher, ['name'.'age'])
Copy the code

Index access operator

T[K], index access operator. But you need to make sure that K extends keyof T

function getValue<T.K extends keyof T> (obj: T, key: K) :T[K] {
  return obj[key]
}
Copy the code

Mapping type

Create a new type from an old type, a mapping type

// Read-only mapping type
type CustomReadonly<T> = {
  readonly [K in keyof T]: T[K]
}
// Optional mapping type
type CustomPartial<T> = {
  [K inkeyof T]? : T[K] }// The mapping type can be null or undefined
type NullUndefinedable<T> = {
  [K in keyof T]: T[K] | null | undefined
}
Copy the code
type CustomPick<T, K extends keyof T> = {
  [P in K]: T[P];
}
interface Bar {
  name: string,
  age: number,
  sex: boolean
}
// Extract the type of the specified key
// Foo = { name: string, age: number }
type Foo = CustomPick<Bar, 'name' | 'age'>
Copy the code
type CustomRecord<K extends string, T> = {
  [P in K]: T;
}
// { name: string, title: string, message: string }
type Bar = CustomRecord<'name' | 'title' | 'message'.string>
Copy the code

Built-in mapping type

Exclude

Exclude

, extracts from T and cannot be assigned to U
,>

// string | boolean
/ / string | Boolean cannot be assigned to the type number
type Foo = Exclude<string | number | boolean.number>
Copy the code

Extract

Extract

Extract the type that can be assigned to U from T
,>

// number
// Only number can be assigned to number
type Foo = Extract<string | number | boolean.number>;
Copy the code

NonNullable

NonNullable

, remove null and undefined from T

type Bar = string | null | undefined
// Foo
type Foo = NonNullable<Bar>;
leta! : Foo;// error
a = null
Copy the code

Nullable

removes null and undefined from attributes based on NonNullable

encapsulation

type NonNullUndefinedable<T> = {
  [K in keyof T]: NonNullable<T[K]>
}
interface Bar {
  name: string | null | undefined
  age: number | null | undefined
  sex: boolean | null | undefined
}
// Foo { name: string, age: number, sex: boolean }
type Foo = NonNullUndefinedable<Bar>
Copy the code

ReturnType

ReturnType

, gets the return value type of T

type BarCall = (a)= > number
// number
type BarReturn = ReturnType<BarCall>
Copy the code

InstanceType

InstanceType

, get the InstanceType of T

class Bar {}
// Bar, the instance type of the constructor of Bar, is Bar type
type Foo = InstanceType<typeof Bar>
Copy the code

Omit

Omit Omit has become a built-in type in TS3.5 version (see below)

// Exclude
      
        Exclude all K types from keyof T
      
// Pick
      
        maps new types with only U attributes
      ,>
// Analog Omit mapping type
type CustomOmit<T, K> = Pick<T, Exclude<keyof T, K>>
Copy the code

lib.d.ts

When you install typescript, you install a set of declaration files lib.dom.d.ts, lib.es2015.core.d.ts, and so on. They contain various common environment declarations that exist in the javaScript runtime and dom

// Lib.dom.d. ts Content excerpt

/** A window containing a DOM document; the document property points to the DOM document loaded in that window. */
interface Window extends EventTarget, WindowTimers, WindowSessionStorage, WindowLocalStorage, WindowConsole, GlobalEventHandlers, IDBEnvironment, WindowBase64, AnimationFrameProvider, WindowOrWorkerGlobalScope, WindowEventHandlers {
  readonly applicationCache: ApplicationCache;
  readonly caches: CacheStorage;
  readonly clientInformation: Navigator;
  readonly closed: boolean;
  readonly crypto: Crypto;
  customElements: CustomElementRegistry;
  defaultStatus: string;
  readonly devicePixelRatio: number;
  readonly doNotTrack: string;
  readonly document: Document;
  / /... more
}
Copy the code

Modify primitive type

Create globals.d.ts global declaration file and modify the original type in the global declaration file

// globals.d.ts

// Modify the Window interface and add the HelloWorld method
interface Window {
  helloworld(): 'helloworld'
}
// Change the static member of Date and add the today method
interface DateConstructor {
  today(): Date
}

/ / use
window.helloworld()
Date.today()
Copy the code

Type of flow

Copy type

Import works with namespace or Module to replicate types.

class Foo {}
const Bar = Foo // Bar is a reference to Foo, not a type
let bar: Bar // error, there is no Bar type
Copy the code
namespace classs {
  export class Foo {}
}
import Bar = classs.Foo // Copy types can be implemented
let bar: Bar // ok
Copy the code

Gets the type of the variable

Use let and typeof to get the typeof the variable itself (if const, typeof gets the literal type)

let foo = 'foo'
letbar! :typeof foo
bar = 'bar' // ok, bar is a string
bar = 123 // error


let foo = [1.2.3]
letbar! :typeof foo
bar = [4.5.6] // ok
bar = ['4'.'5'.'6'] // error, bar is of type number[]
Copy the code

Gets the type of a class (instance) member

class Foo {
  constructor (public name: string) {}}declare let _foo:Foo
// bar is a string, and _foo is an instance of Foo
let bar: typeof _foo.name
Copy the code

Gets the literal type

Literal types can be obtained using the const keyword and typeof

const foo = 'foo'
letbar! :typeof foo
bar = 'bar' // error
bar = 'foo' // ok, foo is of type foo
Copy the code

The namespace

Use namespaces to organize code to avoid naming conflicts. To access the contents inside the namespace, use export to export the contents

namespace Lib {
  export namespace Math {
    export function sum(a: number, b: number) :number {
      return a + b
    }
  } 
}
Lib.Math.sum(1.2)
Copy the code

Namespace alias

Import alias = namespace.namespace. You can create an alias for the namespace

namespace Lib {
  export namespace Math {
    export namespace Geometry {
      export function pythagoreanTheorem(x: number, y: number) :number {
        return x * x + y * y
      }
    }
  } 
}
// When used, there are multiple levels of relationships
Lib.Math.Geometry.pythagoreanTheorem(1.2)
// Use the namespace alias
import Geometry = Lib.Math.Geometry
Geometry.pythagoreanTheorem(1.2)
Copy the code

Use other javascript libraries

Ts requires the type declaration file xx.d.ts when using a class library written in JS. Most libraries usually expose a top-level object that can be represented in a namespace. Let me give you an example.

// node_modules/custom-lib/index.d.ts
export declare namespace Lib {
  export namespace Geometry {
    export function pythagoreanTheorem(x: number, y: number) :number {
      return x * x + y * y
    }
  }
  } 
}
export declare const gravitationalConstant: number
Copy the code
// src/main.ts
import { Lib, gravitationalConstant } from 'custom-lib'

Lib.Geometry.pythagoreanTheorem(gravitationalConstant, 2)
Copy the code

✨ something not mentioned in the manual guide

New syntax for read-only arrays

// The original syntax
const bar: ReadonlyArray<string> = ['Hello'.'World']
// New syntax
const boo: readonly string[] = ['Hello'.'World']
Copy the code

Const type assertion

With const assertions, typescript adds its own literal type to variables. Let me give you an example

// ts automatically deduces the type 'string' for x
// let x: string = '123'
let x = '123'
// x can be reassigned
x = '456'

// Using const assertion adds its own literal type to x
// let x: '123' = '123'
let x = '123' as const
// error, x cannot be reassigned
x = '456'
Copy the code

When using const assertion:

  1. Property of the object literalreadonlyBecomes a read-only property
  2. The array literal becomesreadonly tupleRead-only tuples
  3. Literal types cannot be extended (such as fromhelloType tostringType)
// type '"hello"'
let x = "hello" as const
// type 'readonly [10, 20]'
let y = [10.20] as const
// type '{ readonly text: "hello" }'
let z = { text: "hello" } as const
Copy the code

Const assertions support two syntax, as syntax, and the

syntax can be used in non-TSX files

// type '"hello"'
let x = <const>"hello"
// type 'readonly [10, 20]'
let y = <const> [10.20]
// type '{ readonly text: "hello" }'
let z = <const>{ text: "hello" }
Copy the code

Unknown type

The unknown type is similar to any type. Unlike any type. An unknown type can be assigned to any type, but it must be asserted before being assigned to any other type.

Unknown is useful for apis that want to represent any value, but must perform type checking before using it.

let x:any = 'Hello'
// ok
let y:number = x

let x: unknown = 'Hello'
// error
let y: number = x
// ok, must be asserted to assign to the confirmed type
let z: number = x as number
Copy the code

✨ ✨ ✨ infer

At first, it was hard for me to understand what Infer actually does. Let’s start with two simple examples

type Pro<T> = T extends Promise<infer R> ? Promise<R> : T

type Bar = Pro<Promise<string[] > >// Bar = Promise
      
       , Promise
       
         satisfies the Promise
        
          constraint
        
       []>
      []>
type Boo = Pro<number> // Boo = number
Copy the code

Type Pro, which checks for the generic parameter T, returns the type of the Promise

if it satisfies the Promise constraint, and returns type T otherwise

type Param<T> = T extends (param: infer P) => any ? P : T

type getUser = (id: number) = > object

type Foo = Param<getUser> // Foo equals the parameter type of getUser, number type
type Bar = Param<boolean> // Bar equals Boolean
Copy the code

Type Param, checks the generic parameter T and returns the type of the parameter if it satisfies the constraint (Param: infer P) => any; otherwise, type T is returned

Infer mapping type

The Infer mapping type is built into typescript

type ReturnType

Extracts the type of the return value of the get function

type Foo = ReturnType<(a)= > string[] >// Foo = string[]
Copy the code

Type ReturnType internal implementation

// Customize ReturnType mapping type
type CustomReturnType<T> = T extends(... arg:any[]) => infer P ? P : T 
type Foo = CustomReturnType<(a)= > string[] >// Foo = string[]
Copy the code
type ConstructorParameters

Extract the parameter type of the constructor

class Foo {
  constructor (public name: string.public age: number) {}}type Params = ConstructorParameters<typeof Foo> // [string, number]
Copy the code

Type ConstructorParameters internal concrete implementation

// ConstructorParameters map type
type CustomConstructorParameters<T extends new(... args:any[]) = >any> = T extends new(... args: infer P) =>any ? P : never
type Params = CustomConstructorParameters<typeof Foo> // [string, number]
Copy the code

Tuple type -> union type

How will/string, number into a string | number?

type CustomTuple = [string.number.boolean]

type TupleToUnion<T> = T extends Array<infer P> ? P : never

type CustomUnion = TupleToUnion<CustomTuple> // string | number | boolean
Copy the code

Union type -> Cross type

The examples in TypeScript In Plain English have some problems in the current version (3.6.4).

type Omit

Omit Omit is a new type of mapping in version 3.5 that removes all types of K from the T.

interface Foo {
  name: string
  age: number
}

// Bar = { name: string }
type Bar = Omit<Foo, 'age'>
Copy the code

In previous versions type Omit

= Pick

> can be implemented
,>
,>

reference

Thanks big guys pay ❤️

  • ✨ Sanmao’s blog
  • Understand TypeScript in depth
  • TypeScript Chinese website
  • TypeScript
  • TypeScript Tutorial – ‘infer’ keyword
  • TypeScript union to tuple
  • What are covariance and contravariance?
  • Differences Between TypeScript Type vs Interface