TS

一、ypeScript 的特点

  1. 始于JavaScript,归于JavaScript(最终编译出的依旧是js)
  2. 强大的类型系统(对数据类型的约束)
  3. JS的超集,兼容JS,es6等
  4. 类型注解,提示开发错误

二、安装 TypeScript

  1. 命令行运行如下命令,全局安装 TypeScript:

    1
    npm install -g typescript
  2. 安装完成后,在控制台运行如下命令,检查安装是否成功(3.x):

    1
    tsc -V 

三、基础类型

  1. 布尔值
    1
    2
    3
    let isDone: boolean = false;
    isDone = true;
    // isDone = 2 // error
  2. 数字
    1
    2
    3
    4
    let a1: number = 10 // 十进制
    let a2: number = 0b1010 // 二进制
    let a3: number = 0o12 // 八进制
    let a4: number = 0xa // 十六进制
  3. 字符串
    1
    let name:string = 'tom'
  4. undefined 和 null

    默认情况下 nullundefined 是所有类型的子类型。 就是说你可以把 nullundefined 赋值给 number 类型的变量。

    TypeScript 里,undefinednull 两者各自有自己的类型分别叫做 undefinednull。 它们的本身的类型用处不是很大:

    1
    2
    let u: undefined = undefined
    let n: null = null
  5. 数组
    1
    let list1: number[] = [1, 2, 3]

    第二种方式: 数组泛型,Array<元素类型>

    1
    let list2: Array<number> = [1, 2, 3]
  6. 元组 Tuple

    元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 stringnumber 类型的元组。

    1
    2
    3
    let t1: [string, number]
    t1 = ['hello', 10] // OK
    t1 = [10, 'hello'] // Error

    当访问一个已知索引的元素,会得到正确的类型:

    1
    2
    console.log(t1[0].substring(1)) // OK
    console.log(t1[1].substring(1)) // Error, 'number' 不存在 'substring' 方法
  7. 枚举

    enum 类型是对 JavaScript 标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    enum Color {
    Red,
    Green,
    Blue
    }

    // 枚举数值默认从0开始依次递增
    // 根据特定的名称得到对应的枚举数值
    let myColor: Color = Color.Green // 0
    console.log(myColor, Color.Red, Color.Blue)

    默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1 开始编号:

    1
    2
    enum Color {Red = 1, Green, Blue}
    let c: Color = Color.Green

    或者,全部都采用手动赋值:

    1
    2
    enum Color {Red = 1, Green = 2, Blue = 4}
    let c: Color = Color.Green

    枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:

    1
    2
    3
    4
    enum Color {Red = 1, Green, Blue}
    let colorName: string = Color[2]

    console.log(colorName) // 'Green'

  8. any: 任何类型

    有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量:

    1
    2
    3
    let notSure: any = 4
    notSure = 'maybe a string'
    notSure = false // 也可以是个 boolean

    在对现有代码进行改写的时候,any 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。并且当你只知道一部分数据的类型时,any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:

    1
    2
    3
    let list: any[] = [1, true, 'free']

    list[1] = 100
  9. void:没有任何类型(在函数无返回值时使用)

    某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void

    1
    2
    3
    4
    5
    6
    7
    /* 表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值 */
    function fn(): void {
    console.log('fn()')
    // return undefined
    // return null
    // return 1 // error
    }

    声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefinednull

    1
    let unusable: void = undefined
  10. object

    object 表示非原始类型,也就是除 numberstringboolean之外的类型。

    使用 object 类型,就可以更好的表示像 Object.create 这样的 API。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    function fn2(obj:object):object {
    console.log('fn2()', obj)
    return {}
    // return undefined
    // return null
    }
    console.log(fn2(new String('abc')))
    // console.log(fn2('abc') // error
    console.log(fn2(String))
  11. 联合类型:(’‘ | ’‘) 联合类型(Union Types)表示取值可以为多种类型中的一种
    • 需求1: 定义一个一个函数得到一个数字或字符串值的字符串形式值

      1
      2
      3
      function toString2(x: number | string) : string {
      return x.toString()
      }xxxxxxxxxx 需求2: 定义一个一个函数得到一个数字或字符串值的长度function toString2(x: number | string) : string { return x.toString()}
    • 需求2: 定义一个一个函数得到一个数字或字符串值的长度

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      function getLength(x: number | string) {

      // return x.length // error

      if (x.length) { // error
      return x.length
      } else {
      return x.toString().length
      }
      }
  12. 类型断言:(’‘ as ’‘)

    通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。

    类型断言有两种形式。 其一是“尖括号”语法, 另一个为 as 语法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /* 
    类型断言(Type Assertion): 可以用来手动指定一个值的类型
    语法:
    方式一: <类型>值
    方式二: 值 as 类型 tsx中只能用这种方式
    */

    /* 需求: 定义一个函数得到一个字符串或者数值数据的长度 */
    function getLength(x: number | string) {
    if ((<string>x).length) {
    return (x as string).length
    } else {
    return x.toString().length
    }
    }
    console.log(getLength('abcd'), getLength(1234))
  13. 类型推断

    类型推断: TS会在没有明确的指定类型的时候推测出一个类型
    有下面2种情况: 1. 定义变量时赋值了, 推断为对应的类型. 2. 定义变量时没有赋值, 推断为any类型

    1
    2
    3
    4
    5
    6
    7
    8
    /* 定义变量时赋值了, 推断为对应的类型 */
    let b9 = 123 // number
    // b9 = 'abc' // error

    /* 定义变量时没有赋值, 推断为any类型 */
    let b10 // any类型
    b10 = 123
    b10 = 'abc'

四、接口

inferface 一种能力,约束(约束对象的属性值的类型)

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。我们使用接口(Interfaces)来定义对象的类型。接口是对象的状态(属性)和行为(方法)的抽象(描述)

  1. 可选属性: sex ? : string
  2. 只读属性:readonly id : number

    readonly vs const

    最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly

  3. 函数类型:接口也可描述函数了

    为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型

    1
    2
    3
    4
    5
    6
    7
    /* 
    接口可以描述函数类型(参数的类型与返回的类型)
    */

    interface SearchFunc {
    (source: string, subString: string): boolean
    }

    这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。

    1
    2
    3
    4
    5
    const mySearch: SearchFunc = function (source: string, sub: string): boolean {
    return source.search(sub) > -1
    }

    console.log(mySearch('abcd', 'bc'))
  4. 类类型:

    类实现接口:implements

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    /* 
    类类型: 实现接口
    1. 一个类可以实现多个接口
    2. 一个接口可以继承多个接口
    */

    interface Alarm {
    alert(): any;
    }

    interface Light {
    lightOn(): void;
    lightOff(): void;
    }

    class Car implements Alarm {
    alert() {
    console.log('Car alert');
    }
    }
  5. 一个类实现多个接口: class Cars implements 接口1,接口2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Car2 implements Alarm, Light {
    alert() {
    console.log('Car alert');
    }
    lightOn() {
    console.log('Car light on');
    }
    lightOff() {
    console.log('Car light off');
    }
    }
  6. 接口继承接口:extends
    1
    2
    3
    interface LightableAlarm extends Alarm, Light {

    }

五、类

基于类的面向对象的方式,单例模式也可以理解为是class类的实例,仅有这一个实例

  1. 继承:
    • ​ extends : 派生类(子类)从基类(超类)中继承属性和方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      /* 
      类的继承
      */

      class Animal {
      run (distance: number) {
      console.log(`Animal run ${distance}m`)
      }
      }

      class Dog extends Animal {
      cry () {
      console.log('wang! wang!')
      }
      }

      const dog = new Dog()
      dog.cry()
      dog.run(100) // 可以调用从父中继承得到的方法
    • ​ super: 子类调用父类的方法或构造函数时使用,子类也可重写父类的方法

  2. 类成员的修饰符
    • ​ public: 公开的,默认值,外部可以访问
    • ​ protected:受保护的,类内部和子类中可以访问
    • ​ private:私有的,不公开,只有类内部可以访问
    • ​ readonly : 只读
    • ​ 参数属性:相当于类的简写
  3. 存取器:类属性的获取(get函数)、类属性的改写(set函数)
  4. 类的静态属性: static(实际使用中很多)

    到目前为止,我们只讨论了类的实例成员,那些仅当类被实例化的时候才会被初始化的属性。 我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上。 在这个例子里,我们使用 static 定义 origin,因为它是所有网格都会用到的属性。 每个实例想要访问这个属性的时候,都要在 origin 前面加上类名。 如同在实例属性上使用 this.xxx 来访问属性一样,这里我们使用 Grid.xxx 来访问静态属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    /* 
    静态属性, 是类对象的属性
    非静态属性, 是类的实例对象的属性
    */

    class Person {
    name1: string = 'A'
    static name2: string = 'B'
    }

    console.log(Person.name2)
    console.log(new Person().name1)
  5. 抽象类:抽象类其子类可以实例化(extend继承后再实例)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    /* 
    抽象类
    不能创建实例对象, 只有实现类才能创建实例
    可以包含未实现的抽象方法
    */

    abstract class Animal {

    abstract cry ()

    run () {
    console.log('run()')
    }
    }

    class Dog extends Animal {
    cry () {
    console.log(' Dog cry()')
    }
    }

    const dog = new Dog()
    dog.cry()
    dog.run()

六、函数:匿名函数、命名函数

  1. 函数定义类型
    1
    2
    3
    4
    5
    6
    7
    function add(x: number, y: number): number {
    return x + y
    }

    let myAdd = function(x: number, y: number): number {
    return x + y
    }
  2. 可选参数(?)和默认参数(未传参时为undefind)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    firstName: string='A' 是默认参数,
    lastName?: string 是可选参数

    function buildName(firstName: string='A', lastName?: string): string {
    if (lastName) {
    return firstName + '-' + lastName
    } else {
    return firstName
    }
    }

    console.log(buildName('C', 'D'))
    console.log(buildName('C'))
    console.log(buildName())
  3. 剩余参数(…args:string)
  4. 函数重载:函数名相同,而参数不同的多个函数

七、泛型

在定义函数、接口、类时未预定类型 ,也可以写成其他字母

  1. 函数泛型
  2. 多个泛型参数的函数:<T,V>,使用逗号隔开
    1
    2
    3
    4
    5
    function swap <K, V> (a: K, b: V): [K, V] {
    return [a, b]
    }
    const result = swap<string, number>('abc', 123)
    console.log(result[0].length, result[1].toFixed())
  3. 泛型接口

    在定义接口时, 为接口中的属性或方法定义泛型类型
    在使用接口时, 再指定具体的泛型类型

    1
    2
    3
    4
    5
    interface IbaseCRUD <T> {
    data: T[]
    add: (t: T) => void
    getById: (id: number) => T
    }
  4. 泛型类

    在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型

    1
    2
    3
    4
    class GenericNumber<T> {
    zeroValue: T
    add: (x: T, y: T) => T
    }
  5. 泛型约束:泛型参数直接取属性值会报错,必须先继承接口 ,因为这个泛型根本就不知道它有这个属性
    1
    2
    3
    4
    // 没有泛型约束
    function fn <T>(x: T): void {
    // console.log(x.length) // error
    }

    我们可以使用泛型约束来实现

    1
    2
    3
    4
    5
    6
    7
    8
    interface Lengthwise {
    length: number;
    }

    // 指定泛型约束
    function fn2 <T extends Lengthwise>(x: T): void {
    console.log(x.length)
    }

    我们需要传入符合约束类型的值,必须包含必须 length 属性:

    1
    2
    fn2('abc')
    // fn2(123) // error number没有length属性

八、其他

  1. 声明文件:使用第三方库时,需先引用它的声明文件
  2. 内置对象:可以在TS中当定义好了的类型使用
    1
    2
    3
    4
    5
    6
    7
    ECMAScript:

    NumberBooleanStringDateRegExpError

    BOM 和 DOM 的内置对象

    Window、Document、HTMLElement、DocumentFragment、Event、NodeList