TS实战

215 阅读1分钟
初体验:安装和运行ts
$ npm install -g typescript
$ tsc -v 
$ tsc helloworld.ts

一、ts类型声明

Boolean \ Number \ String \ Array \ Function \ Object \ Symbol \ undefined \ null
void \ any \ never \ 元组 \ 枚举 \ 高级类型

类型注解, 就是类型声明
1.基础类型
// 原始类型
let isDone: boolean = false
let count: number = 10
let name: string = 'smile'

// symbol - 具有唯一的值
let s1: symbol = Symbol()
let s2 = Symbol()
console.log(s1 === s2) //error

// Array 类型
let arr1: number[] = [1, 2, 3]
let arr2: Array<number> = [1, 2]   // 泛型语法
let arr2: Array<number | string> = [1, 2, '4']  // 联合类型

// Tuple 元组
一种特殊的数组,它限定了元素的类型和个数
let tuple: [number, string] = [0, '1']
let tuple: [number, string] = ['good', '1']  //error
// void
没有任何返回值的类型
let noReturn = () => {}
function warnUser(): void {
    console.log("message")
}

// undefined, null
是任何类型的子类型
let un: undefined = undefined
let nu: null = null
// any 类型
全局超级类型

// Unknown 类型any类型的补充,
unknown 类型只能被赋值给 any 类型和 unknown 类型本身,当中间类型赋值变化时,会error

// never
永远不会有返回值的类型
2.对象和函数类型
// 对象类型
let stu: object = { name: '张三', age: 18 }
console.log(stu)

注: 一般对象类型不会这样申明
而是直接写让 TS 做自动类型判断或者,如接口等
// 函数类型
1) 返回值类型可省略,利用了ts的类型推断功能
2) 有多种声明方式,一般靠类型推断,或者是接口来描述

let add = (x: number, y: number):number => return x + y
3.枚举类型
枚举 - 一组有名字的常量集合
解决的问题:  1)可读性差,很难记住数字的含义;  2)可维护差,牵一发动全身
// 01 - 数字枚举
enum Role {
    Reporter = 1,
    Owner,
}
console.log(Role.Reporter)

// 02 - 字符串枚举
enum Message {
    Success = '成功了',
    Fail = '失败了'
}

// 03 - 常量枚举
const enum Month {
    Jan,
    Feb,
    Mar,
    Apr = Month.Mar + 1,
    // May = () => 5
}
let month = [Month.Jan, Month.Feb, Month.Mar]

// 04 - 异构枚举(数字和字符串的混合)
enum Enum {
  A,
  B,
  C = "C",
  E = 8,
}

----------
// 枚举成员
1) 枚举成员的值是不可修改的
2) 常量成员和计算成员

二、ts高级类型

1.交叉类型
2.联合类型
3.类型保护与区分类型

类型别名 字面量类型 可辨识联合 this类型 索引类型 映射类型


4.ts断言

1.类型断言:

// “尖括号” 或者 as语法
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;

2.非空断言

x! --- 将从x值域中排除 null 和 undefined

1) 忽略 undefinednull 类型
function myFunc(maybeString: string | undefined | null) {
  const onlyString: string = maybeString;   // Error (严格语法时报错)
  const ignoreUndefinedAndNull: string = maybeString!; // Ok
}

2) 调用函数时忽略 undefined 类型
type NumGenerator = () => number;

function myFunc(numGenerator: NumGenerator | undefined) {
  const num1 = numGenerator();    // Error
  const num2 = numGenerator!();   //OK
}

3.确定赋值断言

通过 let x!: number; 确定赋值断言,Ts编译器就会知道该属性会被明确地赋值

三、TS接口

1. 对传入参数进行约束
2. 对对象类型声明的描述
3. 对类里面的属性、方法进行声明约束,实现这个接口的类必须实现接口里面的属性和方法
// 声明接口
interface TypeA {
   ...
}

// 声明a为TypeA类型并赋值
let a: TypeA = { … }

1.可选属性 ?
2.只读属性 readonly
3.任意属性 可以使用 [propName: string]: any 定义任意属性名取string类型值,属性值取any类型值。
interface Person {
    name: string;
    age?: number;
    readonly x: number;
    [propName: string]: any;
}
1.属性类型接口
// 接口描述对象User
interface User {
    name: string
    age?: number
    readonly isMale: boolean
    say: Say
}
const getUserName = (user: User) => user.name
2.函数类型接口
// 接口描述函数类型Say
interface Say {
    (words: string) : string
}
3.通用类型接口
// 接口描述通用类型
interface Config {
   width?: number;
   [propName: string]: any;
}
不确定接口个数时,这样Config可以有任意数量的属性
4. 类类型接口

实现(implements)

一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些公有的特性,这时候可以吧特性提取成接口,用implements关键字实现

interface Watch{
   watch():string
} 
class Tv implements Watch {
   watch() { …… }
}
class Phone implements Watch {
   watch() { …… }
}

interface Other{
   call();
   play();
} 
class Phone implements Watch, Other {
   private watch() { …… }
   call() { …… }
   play() { …… }
}

一个类可以实现多个接口,但只能extends自一个类(接口用于对类的一部分行为的抽象,不包括具体的实现)接口只能描述类的公共(public)部分

  1. 实例类类型接口(只会对实例的属性进行限制,方法也是类的属性. 接口描述了类的公共部分)
//因为eat也可以说是一个属性
interface Animal {
  name: string
  eat(str: string): void
}

class Dog implements Animal {
  private age: number = 2 
  //接口来限制类(类中必须有name属性和eat方法)
  //类中也可以有其他的属性
  constructor(public name: string) {}
  eat(str: string) {
    console.log(this.name + '吃' + str)
  }
}
let dog: Dog = new Dog('狗')
dog.eat('狗粮')
  1. 构造器与静态类接口
// 定义了两个接口, ClockConstructor为构造函数所用, ClockInterface为实例方法所用
// 定义一个构造函数 createClock,它用传入的类型创建实例

interface ClockConstructor {
    new (hour: number, minute: number): ClockInterface
}
interface ClockInterface {
    tick()
}

function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("beep beep")
    }
}
class AnalogClock implements ClockInterface {
    constructor(h: number, m: number) { }
    tick() {
        console.log("tick tock")
    }
}

let digital = createClock(DigitalClock, 12, 17)
let analog = createClock(AnalogClock, 7, 32)

5. 混合类型接口
//这个混合类型限制的变量本身是个函数,但是有reset方法和interval属性
interface Counter {
  (start: number): number
  interval: number
  reset(): void
}

function getCounter(): Counter {
  let counter: Counter = function (start: number): number {
    return start++
  } as Counter
  counter.interval = 123

  counter.reset = function () {
    this.interval = 0
  }
  return counter
}

let c = getCounter()
c(10)
c.reset()
console.log(c.interval)
// 混合类型接口 -- 单例模式
interface Lib {
    (): void;
    version: string;
    doSomething(): void;
}
let lib = (() => {}) as Lib
lib.version = '1.0.0'
lib.doSomething = () => {}
// 混合类型接口 -- 多例模式
interface Lib {
    (): void;
    version: string;
    doSomething(): void;
}
function getLib() {
    let lib = (() => {}) as Lib
    lib.version = '1.0.0'
    lib.doSomething = () => {}
    return lib;
}
let lib1 = getLib()
lib1()
let lib2 = getLib()
lib2.doSomething()
6. 接口扩展 - extends
// 可以继承多个接口
interface VIPUser extends User, SupperUser {
    broadcast: () => void
}
7. 继承类类型接口

类可以当作接口来使用,也可以被接口所继承

class Control {
  private state: any
}

interface SelectableControl extends Control {
  select(): void
}

class Button extends Control implements SelectableControl {
  select(): void {}
}

class Radio extends Control {
  select(): void {}
}

三、TS类

1.属性与方法
class Person {
    // 静态属性
    static max: number = 100
    // 成员属性
    name: string

    // 构造函数 - 类被初始化时,自动执行方法
    constructor(n: string) {
      this.name = n
     }

    // 静态方法
    static getClassName() {
        return "Class name is Greeter";
    }

    // 成员方法
    run(): void {
        console.log(this.name)
    }
}

// 构造函数就是在类被初始化的时候,自动执行的一个方法
// 我们通过这个构造方法经常作很多需要提前完成的工作,比如显示页面前我们要从后台得到数据
// 01 实例类类型
// 类本身就是一种类型,类的名字可以直接作为类型名。
// 声明p为Person类型并赋值(默认为any)
let p: Person = new Person('张三') 


// 02 静态类类型(类本身)
// typeof Person是表明该对象是一个Person类的类型,而不是Person的实例类型

let Person2: typeof Person = Person
Person2.max = 150
console.log(Person === Person2)

2.类的继承 extends、super

在ts中,我们可以通过 extends 关键字来实现继承,是类与类的层次模型(一个类只能继承一个父类)

// 基类  /  超(父)类
class Person {
    public name: string
    constructor(name: string) {
        this.name = name
    }
    ...
}


// 派生类  /  子类
class Student extends Person {
  age: number
  constructor(name: string, age: number) {
    super(name)  // super 继承基类的构造器,并向基类的构造器传参,super必须写在第一行
    this.age = age
  }
  work() {
    console.log(this.age)
  }
}
let s = new Student('李四', 18)
s.run()
s.work()
3.类的修饰符(控制类成员的可访问性)
public:    公有, 在类、子类、类外部都可以访问
protected: 保护, 在类、子类里可以, 在类的外部无法访问
private:   私有, 在类里面可以访问, 在子类和类的外部无法访问
readonly:  只读, 在声明时或构造函数里被初始化

在子类中通过super调用父类原型的属性和方法时,也只能够访问到父类的publicprotected方法,否则会报错
class Animal {
  private name: string
  readonly age: string
  constructor(theName: string) {
    this.name = theName
  }
}
4.寄存器

TS 中也可以对一个属性时用 get 和 set 方法对一个属性内部的获取和赋值进行拦截

let passcode = 'secret passcode'

class Employee {
  private _fullName: string
  get fullName(): string {
    //对fullName属性进行拦截
    return this._fullName
  }
  set fullName(newName: string) {
    if (passcode && passcode == 'secret passcode') {
      this._fullName = newName
    } else {
      console.log('Error: Unauthorized update of employee!')
    }
  }
}

let employee = new Employee()
employee.fullName = 'Bob Smith'
if (employee.fullName) {
  alert(employee.fullName)
}

5.静态属性和静态方法

静态属性和静态方法必须使用 -类名- 调用。

静态方法调用不了实例方法和实例属性。(静态域的加载是在解析阶段,实例化是在初始化阶段)

class Xiaogege {
   age: number = 28
   static name:string = ‘静态属性’;
   static say() {
      console.log(‘静态方法’)
      console.log(this.name)
      console.log(this.age)     // 错误
   }
}

console.log(Xiaogege.name)
Xiaogege.say()        

let xiaoge = new Xiaogege()      
xiaoge.say()                  // 错误
console.log(xiaoge.name)      // 错误
6.抽象类

使用 abstract 声明的类,为抽象类(abstract定义抽象类和抽象方法)

不能直接被实例化,只能被其他类所继承

在工作中,我们也会把这样的需求用接口来实现

abstract class Animal { 
   name: string;
   constructor (name:string) { this.name = name }
   abstract  eat () : void
   abstract  eat () : {        // error
      console.log(`${this.name}吃骨头`
   }
}
class Gou extends Animal { 
   constructor (name:string) { super(name)}
   eat () : void{ console.log(`${this.name}吃骨头`) }
   abstract  say () : void         // error
}
const gou : Gou = new Gou(‘汪汪’)
gou.eat()
7.多态

类类型(class) -- 多态 => 重写方法

父类定义一个方法不实现,让子类去实现,每个子类的该方法有不同的表现

class Animal { 
   name: string;
   constructor (name:string) { this.name = name }
   eat ():void
}
class Gou extends Animal { 
   constructor (name:string) { super(name)}
   eat (){ console.log(`${this.name}吃骨头`) }
}
class Mao extends Animal { 
   constructor (name:string) { super(name)}
   eat () { console.log(`${this.name}吃鱼干`) }
}
const gou : Gou = new Gou(‘汪汪’)
gou.eat()
const mao : Mao = new Mao(‘喵喵’)
mao.eat()