ts基础教程(二)

类型推论

如果没有明确的指定类型,那么ts会依照类型推论的规则推断出一个类型

1. ts更严格

虽然ts是js超集,但并不意味着所有js的用法都能照搬,js可以跨类型给变量赋值,但是ts不行。

//这种写法ts会报错,js是可以的
let str="lengyuexin";
str=1024

2. ts推论结果

ts根据第一次变量赋值的类型进行推导,实际上,上述代码和下边代码是等价的

//这个string类型就是ts根据第一次变量赋值的类型进行推导得到的结果
let str:string="lengyuexin";
str=1024

3. 定义但不赋值

如果变量定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查


//这不是什么好习惯,像极了脱缰的野马
//当你试图削弱ts静态类型检查优势的时候,说明你依旧过度依赖js写法
let str;
str="lengyuexin"
str=1024;
str=[]
str=false;
str={}

联合类型

联合类型表示取值可以为多种类型中的一种,不同类型使用管道符|分隔,有点像或的感觉。

1. 使用示例

let age:string|number;
age=18;//ok
age="18"//ok
age=false//error  ,非联合类型子类型的不可以

2. 访问联合类型的属性和方法

这有点像交集,只有共有的属性和方法才可以被访问

//这样代码会报错,number类型是没有length属性的
function getLength(something: string | number): number {
    return something.length;
}
//但是这样就可以,字符串和数组都有length属性
function getLength(something: string | number[]): number {
    return something.length;
}

//也可以像这样访问公共方法
function getStr(something: string | number[]): string {
    return something.toString();
}

3. 联合类型的类型推论

类型推论的规则对于联合类型依旧适用

let age: string | number;
age = '20';
console.log(age.length); // 正常运行
age = 20;// 第一次赋值,age已经被ts视为string
console.log(age.length); // Property 'length' does not exist on type 'number'.

类型断言

类型断言(Type Assertion)可以用来手动指定一个值的类型,ts中的断言有以下两种使用方式:

  1. <类型>值
  2. 值 as 类型 在tsx(React jsx的ts版)语法中必须用这种

1. js中的断言

console.assert(expression, message),expression为false情况下,会在控制台打印message

console.assert(true,"hello")//正常运行,无输出
console.assert(false,"hello")//VM2587:1 Assertion failed: hello

2. ts中的断言

联合类型的变量尚未确定类型的时候,只能访问联合子类型共有的属性或方法,断言可以突破这个限制


//这样会报错
function getLength(something: string | number): number {
    if (something.length) {
        return something.length;
    } else {
        return something.toString().length;
    }
}

//用断言就ok

function getLength(something: string | number): number {
  if ((<string>something).length) {
      return (<string>something).length;
  } else {
      return something.toString().length;
  }
}

//类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的
// 这样会报错,boolean不属于联合类型string | number
function toBoolean(something: string | number): boolean {
    return <boolean>something;
}

内置对象

ts核心库定义文件定义了所有浏览器环境需要用到的类型,内置类型判断,NodeJS的需要安装@types/node

1. esma

Boolean、Error、Date、RegExp 等。

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
//...

2. bom/dom

Document、HTMLElement、Event、NodeList 等。

let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
  // Do something
});

3. 内置类型检查

//报错 e被推断成 MouseEvent,而 MouseEvent 没有 targetCurrent 属性
document.addEventListener('click', function(e) {
    console.log(e.targetCurrent);
});

类与接口

上一篇文章对对象类型进行约定的时候使用了接口,实际上,接口还可以用于对类的某个行为进行抽象。而至于类,可用于对接口的特定规范进行实现,也就是常说的某接口的实现类。类可以继承(单继承),接口也可以(多继承),面向对象的思想在ts中体现的更明显。

1. 类实现接口

接口可以是类某一行为的抽象,类可以实现多个接口。将动物视为一个类,哺乳动物为动物的子类。猫和狗都属于哺乳动物,都具有发出叫声这一行为。而这一行为,就可以抽象为接口。

//定义动物类 若有实例属性外部初始化,需要构造函数
class Animal {
  color: string;
  constructor(color: string) {
    this.color = color;
  }
  //也可以声明实例方法
  yell(str:string):string{
    return str
  }
}


//*******************//

// 以下示例基于最原始的 class Animal {}


//将叫声这一行为抽象成接口
interface AnimalYell{
    yell():void
}


//定义动物类
class Animal {
}


// 定义Mammal类,继承Animal类,实现AnimalYell接口

class Mammal extends Animal implements AnimalYell {
    yell() {
        console.log('动物发出叫声');
    }
}

//Dog,Cat类继承父类Mammal,重写yell方法
// 这里也可以直接让Dog,Cat实现AnimalYell接口
//之所以继承,是更大程度的复用父类Mammal的特点(属性和方法)

class Dog extends Mammal {
    yell(){
         console.log('汪汪...');
    }
}

class Cat extends Mammal {
    yell(){
         console.log('喵喵...');
    }
}



// 一个类可以实现多个接口
interface AnimalWalk{
    walk():void
}

//Snake实现AnimalWalk,AnimalYell两个接口\
//实现接口要实现每个接口中的方法
//若以下代码缺少yell或walk的实现就会报错
class Snake implements AnimalWalk,AnimalYell{
    yell(){
        console.log("嘶嘶...")
    }

    walk(){
        console.log("爬行...")
    }
}

new Mammal().yell()//动物发出叫声
new Dog().yell()//汪汪...
new Cat().yell()//喵喵...
new Snake().yell()//嘶嘶...
new Snake().walk()//爬行...

2. 接口继承接口

类可以继承,接口也可以。实际上,ts中接口还能继承类,这在其他面向对象语言中并不允许(如java)。


//将手机功能抽象为一个接口
interface BasePhoneFunction {
  call(): string; //打电话
  send(message: string): string[]; //群发短信
}

// 在基础功能上,还有其他功能
interface PhoneFunction extends BasePhoneFunction {
  game(): void; //打游戏
  listen(): void; //听音学
  watchTV(...tvName:string[]): void; //看电视
  note(): string[]; //便签
}

class Phone implements PhoneFunction {
  call() {
    return "hi, tom,when are we going to coding?";
  }

  send(message: string) {
    console.log(message);
    return ["tom", "jack", "july"];
  }

  game(){
    console.log("打游戏")
  }
  listen(){
    console.log("听音乐")
  }
  //注意这里的rest参数用法,形参名不必和接口定义的形参名一致 tvName
  watchTV(...args:string[]){
    console.log(`看${args.toString()} `)
  }

  note(...args:string[]){
    return [...args]
  }

}


const p=new Phone()
console.log(p.call())//hi, tom,when are we going to coding?
p.send("新年快乐") //新年快乐
console.log(p.send("新年快乐"))// 新年快乐 ["tom", "jack", "july"]
p.game()//打游戏
p.listen()//听音乐
p.watchTV("琅琊榜","将夜")//看琅琊榜,将夜
console.log(p.note("吃饭","睡觉","打豆豆"))//[ '吃饭', '睡觉', '打豆豆' ]

3. 接口继承类

这看起来有些不可思议,接口怎么能继承一个类呢?ts毕竟是ts有它的特殊性,就像js对象不支持反向映射,但是ts的枚举本质就是对象,就能支持反向映射(基于数字的枚举)

//注意,这种写法不能缺少构造函数
class Person {
  eat: string;
  drink: string;
  constructor(eat:string,drink:string){
    this.eat=eat
    this.drink=drink
  }
}

//接口PersonAction 继承类Person , 代码正常运行
//接口PersonAction能有什么用呢?定义对象啊
interface PersonAction extends Person {
  play: string;
}

const p: PersonAction={
  eat:"酸菜鱼",
  drink:"可乐",
  play:"coding"
}

3.1 接口继承类的本质

实际上,接口能继承类是因为ts中类具有特殊性,既是用于实例化操作的类,又是某种类型规范


class Person {
  eat: string;
  drink: string;
  constructor(eat:string,drink:string){
    this.eat=eat
    this.drink=drink
  }
}

//这是最常规的用法了
new Person("酸菜鱼","可乐")

//还可以将其作为一种类型,定义对象
const p:Person={
    eat:"酸菜鱼",
    drink:"可乐"
}


之前定义对象都是用的接口,现在可以用类,这说明ts中的类和接口存在共性。回到最初,接口继承类,来探寻答案

class Person {
  eat: string;
  drink: string;
  constructor(eat:string,drink:string){
    this.eat=eat
    this.drink=drink
  }
}

//其实这种写法等价于下边这种写法
interface PersonAction extends Person {
  play: string;
}


//=>step1, 注意看BasePerson和Person的异同
interface BasePerson {
     eat: string;
     drink: string;
}
//=>step2
interface PersonAction extends BasePerson {
  play: string;
}

//这样依旧ok,Person类实际被拆解成了接口
 const p:PersonAction={
  eat:"酸菜鱼",
  drink:"可乐",
  play:"coding"
}


3.2 同与不同

经过上述探索,得出结果:ts接口继承类本质是接口继承接口。但是被转换成接口的类(BasePerson),和原始的类(Person),差异在哪?再看一个例子。


 class Person {
  //实例属性
  eat: string;
  drink: string;
  constructor(eat:string,drink:string){
    this.eat=eat
    this.drink=drink
  }
  //实例方法
  log():void{
      console.log("log...")
  }

  //静态属性
  static age:number=18;
  //静态方法
  static log():void{
    console.log("static log...")
  }
}

console.log(Person.age)//18
Person.log()//static log...


// 该类被接口继承的时候 发生的变化

interface BasePerson {
  eat: string
  drink: string
  log():void
}

  • 没有构造函数。 这其实很正常,定义类型规范而已,又不是实例化,要构造函数干嘛?
  • 只有实例属性和实例方法会被保留, 静态属性或静态方法不被继承

访问修饰符

java中访问修饰符4种(public/protected/default/private),ts中的访问修饰符3种(public/protected/private)

1. public

public是权限最高的,默认就是public,可自由的访问程序里定义的成员

// 默认写法
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

// 等价于这种写法
class Person {
 public name: string;
 public constructor(name: string) {
    this.name = name;
  }
}

console.log(new Person("tom").name)//tom

2. private

class Person {
 private name: string;
 private constructor(name: string) {
    this.name = name;
  }
}

console.log(new Person("tom").name)//报错,外部访问不到

3. protected

class Person {
  protected name: string;
  protected constructor(name: string) {
    this.name = name;
  }
}

class Student extends Person {
  constructor(name: string) {
    super(name); //调用父类构造函数
  }

  getName(): string {
    //Student是Person的子类,所以可以拿到protected修饰的name属性
    return this.name;
  }
}

console.log(new Student("tom").getName()); //tom

参考链接

  • 掘金ts小册,https://juejin.im/book/5da08714518825520e6bb810
  • ts中文手册,https://zhongsp.gitbooks.io/typescript-handbook
  • ts入门教程,https://ts.xcatliu.com
原创文章 451 获赞 859 访问量 28万+

猜你喜欢

转载自blog.csdn.net/qq_42813491/article/details/104701187