2023最全Typescript 基础合集🎆

119 阅读9分钟

横素波而滂流,干青云而直上

1. 语言概览

Typescript是Javascript语言的超集.Javascript程序本身就是合法的Typescript程序。Javascript语言中的所有语法均可以在Typescript语言中使用并具有完全相同的意义。

一些基础语法按下不表,提两个比较常用且新增的语法:

1.1 可选链运算符

Typescript3.7及以上版本中可以直接使用该属性

可选链运算符由一个问号和一个点号组成,即"?."。可选链运算符有以下三种语法形式:

  • 可选的静态属性访问
obj?.prop

在该语法中,如果obj的值为undefined或null,那么表达式的求值结果为undefined;否则,表达式的求值结果为obj.prop。

  • 可选的计算属性访问
obj?.[expr]

同上,obj的值为undefined或null,那么表达式的求值结果为undefined;否则,表达式的求值结果为obj.prop。

  • 可选的函数调用或方法调用
fn?.()
  • 短路求值 如果可选链运算符左侧操作数的求值结果为undefined或null,那么右侧的操作数不会再被求值,我们将这种行为称作短路求值。
let x = 0;
let a = undefined;

a?.[++x]; // undefined
x; // 0

值得一提的是,二元逻辑运算符“&&”和“||”也具有短路求值的特性。

1.2 空值合并运算符

a ?? b

该语法中,当且仅当“??”运算符左侧操作数a的值为undefinednull(),返回右侧操作数b;否则返回左侧操作数a。

2. 类型基础

2.1 Typescript中的原始类型包含

  • boolean
      const yes:boolean = true;
      const no:boolean = false;
    
  • string
    const foo:string = 'foo';
    
  • number
    const bin:number = 10;
    
  • bigint
    const bin:bigint = 0b1010n;
    
  • symbol
    const key:symbol = Symbol();
    const  aUnique: unique symbol = Symbol.for('aUnique');
    
  • undefined
    // undefined 类型只能包含一个值,即 undefined
    const foo:undefined = undefined;
    
  • null
    const foo:null = null;
    

注意: “--strictNullChecks”,即严格的null检查模式。虽然该编译选项的名字中只提及了null,但实际上它同时作用于undefined类型和null类型的类型检查。默认情况下“--strictNullChecks”编译选项没有被启用,这时候除了尾端类型外的所有类型都是Nullable类型。也就是说,除尾端类型外的所有类型都能够接受undefined值和null值。

// --strictNullChecks = false;

let m1: string = undefined;
let m2: number = null;
  • void(表示某个值不存在, 该类型用作函数的返回值类型。)

    function log(message: string):void {
        console.log(message);
    }
    
  • 枚举类型

    enum Direction {
        Up, // 0 
        Down, // 1
        Left, // 2
        Right // 3
    }
    

    const 枚举类型 有时候我们不需要使用枚举成员值到枚举成员名的反向映射,因此没有必要生成额外的反向映射代码,所以可以使用const枚举类型

    enum E {
        name,
        age = 1
    }
    // 编译之后
    var E;
    (function (E) {
        E[E["name"] = 0] = "name";
        E[E["age"] = 1] = "age";
    })(E || (E = {}));
    
    const enum E1 {
        name,
        age
    }
    E1.age; 
    // 编译之后 不会有映射关系代码,而是直接做替换;
    1 /* E1.age */;
    
  • 字面量类型 Typescript 支持将字面量作为类型使用,我们称之为字面量类型。每一个字面量类型都只有一个可能的值。

2.2 顶端类型

  • any 所有类型都是any类型的子类型。我们可以将任何类型的值赋值给any类型;同时允许将any类型的值赋值给任何其他类型。

    let x: any;
    let a :boolean = x;
    let b:number = x;
    
  • unknown 任何其他类型都能赋值给unknown类型,该行为与any行为一致;但是 unknown类型只允许赋值给any类型或者unknown类型。

    let x: unknown;
    let a : any = x;
    let b: unknown = x;
    
    // 会产生编译错误
    let c : boolean = x; 
    

2.3 尾端类型(never)

尾端类型是所有其他类型的子类型。

let x :never;
let a : number = x;

正如尾端类型处于最底层,没有类型是尾端类型的子类型,除never类型外,其他类型都不能赋值给never类型。

let x : never ;
x = true // 错误
x = 'ssss' // 错误

2.4 数组类型

2.4.1 类型

const list:number[] = [1,2,3];
const list1: (string | number)[] = ['1',2,3];
const list2: Array<number> = [1,2,3];
const list3: Array<number | string> = ['1',2,3,4];

2.4.2 只读数组

只读数组不允许修改

const rList: ReadonlyArray<number>  = [1,2,3];
const rList1: readonly number[] = [1,2,3];
const rList2: Readonly<number[]> = [1,2,3];

2.5 对象类型

2.5.1 Object类型

Object类型注意和Object()构造函数区分;下例是Object()构造函数的类型定义:

interface ObjectConstructor {
    readonly prototype: Object;
    // 省略了其他成员
}
declare var Object: ObjectConstructor

由此可以看出 Object类型是Object.prototype的类型。

2.5.2 object类型

object类型标识非原始类型,即对象类型。关注点不是该对象类型具体包含了哪些属性,因此不允许读取和修改object类型上的自定义属性。

const obj11:object = { name: 'name', age: 18 };
obj11.name  // 编译报错:类型“object”上不存在属性“name”

2.5.3 对象类型字面量

const obj: { name: string, age: number } = { name: '111', age: 18 }

const obj1: { name: string, age?:number } = {
    name: '111'
}

2.6 函数类型

function add( num1: number, num2: number ) {
    return num1 + num2;
}

function add1(x:number, y?:number, z?:number) {
    return x + (y??0) + (z ?? 0)
}
// z?:number=0 报错:参数不能包含问号和初始化表达式。
function add2( x:number, y?:number, z?:number = 0 ) {

    return x + (y??0) + (z ?? 0)
}

可选参数必须放在函数参数末尾并且可选参数不需要初始化值。

2.7 接口

类似于对象类型字面量,接口类型也能够表示任意的对象类型。不同的是接口类型能够给对象类型命名以及定义类型参数。

interface IPoint {
    x: number,
    y: number
}

const point: IPoint  = {
    x: 1,y: 1
}

2.7.1 字符串索引签名

一个接口中最多只能定义一个字符串索引签名。字符串索引签名会约束该对象类型中所有属性的类型。

interface IA {
    [prop: string] : number,
    a: number;
    b: 0
}

interface IA {
    [prop: string] : number,
    a: number;
    b: string // 错误❌ 类型“string”的属性“b”不能赋给“string”索引类型“number”
}

2.7.2 数值索引签名

一个接口中最多只能定义一个数值索引签名。数值索引签名约束了数值属性名对应的属性值的类型。


interface INumA {
    [prop: number] : number
}

const arr: INumA = [1];

如果接口中同时存在数值索引签名和字符串索引签名,则数值索引签名的类型要能赋值给字符串索引签名类型。

interface IStrNum {
    [prop: string]: number,
    [prop:number] : number
}

interface IStrNum {
    [prop: string]: number,
    [prop:number] : number // 错误❌:“number”索引类型“string”不能分配给“string”索引类型“number”
}

2.7.3 只读属性和方法

readonly 修饰符只允许在属性签名和索引签名上使用。


interface IRead {
    readonly a:  number
}

const iread:IRead = {
    a: 1
}
iread.a = 2; // 错误❌: 无法分配到 "a" ,因为它是只读属性。

如果接口中既定义了只读索引签名又定义了非只读属性签名,那么非只读属性签名定义的属性依旧是非只读的,除此之外的所有属性都是只读的。

interface IRead {
    readonly [prop: string]:  number,
    x: number
}

const iread:IRead = {
    x: 1,
    y: 2
}
iread.x = 3;
iread.y = 3; // 错误❌: 类型“IRead”中的索引签名仅允许读取

2.7.4 接口的继承

接口的继承需要使用extends关键字,一个接口可以同时继承多个接口没付接口名之间使用逗号分隔。

interface IStyle {
    color: string
}

interface IShape {
    name: string
}

interface ICircle extends IStyle, IShape {
    radius: number
}


const circle1: ICircle = { 
    color: 'red',
    radius: 0.5,
    name: 'circle'

}

如果子接口与父接口之间存在同名的类型成员,那么子接口中的类型成员具有更高的优先级。同时,子接口与父接口中的同名类型成员必须是类型兼容的。

interface IStyle {
    color: string
}

interface IShape {
    name: string
}

interface ICircle extends IStyle, IShape {
    radius: number,
    name: number // 错误❌:接口“ICircle”错误扩展接口“IShape”。
  // 属性“name”的类型不兼容。
    // 不能将类型“number”分配给类型“string”
}

2.8 类型别名

类型别名声明则能够为Typescript中的任意类型命名。

type StringType = string;
type Point = { x: number, y : number }

2.9 类

2.9.1成员可访问性

  • public
class Base {
    public a :string = "";
}

class Derived extends Base {
    public b () {
        return this.a // 允许访问
    }
}
  • protected

受保护成员允许在当前类的内部和派生类的内部访问,但是不允许在当前类的外部访问。类的受保护成员使用protected装饰符标识。

class Base {
    protected x :string = "";
    a() {
        return this.x; // 允许访问
    }
}

class Derived extends Base {
    public b () {
        return this.x // 允许访问
    }
}

const base = new Base ();
base.x // 不允许访问
  • private

    只允许在当前类的内部类访问,其余情况都不允许访问。

class Base {
    private x :string = "";
    a() {
        return this.x; // 允许访问
    }
}

class Derived extends Base {
    public b () {
        return this.x // 不允许访问
    }
}

const base = new Base ();
base.x // 不允许访问
const derived = new Derived();
derived.x // 不允许访问

2.9.2 实现接口

实现接口关键字 implements

interface Color {
    color: string;
}

interface Shape {
    area(): number;
}

class Circle implements Shape,Color {
    color: string = 'red';
    radius: number = 1;
    area():number {
        return Math.PI * this.radius * this.radius;
    }
}

2.9.3 抽象类和抽象成员

抽象成员不允许包含任何实现代码


abstract class Ab1 {
    abstract message: string  = ''; // 属性“message”不能具有初始化表杰式,因为它标记为摘要
}

abstract class Ab1 {
    abstract message: string; 
}

抽象类可以继承抽象类也可以继承派生类,派生类继承抽象类要实现抽象类中的抽象成员。


abstract class Ab1 {
    abstract message: string ;
}

class AbE extends Ab1 {
    message = '1'
}

class AbE extends Ab1 { // 不允许 非抽象类“AbE”不会实现继承自“Ab1”类的抽象成员“message”
    // message = '1'
}

3. 类型进阶

3.1 泛型

function identity1<T>(arg:T) : T {
    return arg;
}

const foo1 = identity1<string>('foo');

3.1.1 可选的类型参数

如果一个形式类型参数没有定义默认类型,那么它是一个必选类型参数;反之,如果一个形式类型参数定义了默认类型,那么踏实一个可选的类型参数。并且必选的类型参数不允许出现在可选类型参数之后

<T = boolean, U> // 不允许
<U, T = boolean> // 正确

3.2 联合类型

一个值的类型可以为若干类型之一

type numericType = number | bigint;

numericType 定义了一个值的类型既可能是number类型又可能是bigint类型。

interface Circle {
    area: number;
    radius: number;
}

interface Rect {
    area: number;
    width: number;
    height: number;
}

type Shape = Circle | Rect;

declare let shape:Shape;
shape.area
shape.radius // 不存在

3.3 交叉类型

一个值可以同时属于多个类型

interface Clickable {
    click(): void;
} 

interface Focusable {
    focus(): void;
}


type T  = Clickable & Focusable;

declare let t:T;
t.click(); // 存在
t.focus(); // 存在

原始类型的交叉类型

type U = boolean & number & string; // U 为 never类型

3.4 交叉类型和联合类型

  • 优先级 & 优先级高于 |

    A & B | C & D 等价于 (A & B) | (C & D)
    
  • 分配率性质

    A & (B | C) 等价于  (A & B) | (A & C)
    
    // 利用上面性质推导下面类型
    T = ( string | 0 ) & ( number | 'a' ) 
      = string & number | string & 'a' | 0 & number | 0 & 'a'
      = never | 'a' | 0 | never
      = 'a' | 0
    

3.5 类型断言

  • <T>类型断言
      function add(a:any ) {
          return <number> a + 1;
      }
    
  • as T 类型断言
      function add(a:any) {
          return a as number + 1;
      }
    
  • !类型断言

非空类型断言的一部分,从某个类型中剔除 undefined和 null类型。

  function isDefined(value:any) {
     return value !== undefined && value !== null;
 }
 function getLength(v:string | undefined) {
     if(!isDefined(v)) {
         return 0;
     }
     return v!.length;
 }