TypeScript学习笔记总结---第一篇

365 阅读6分钟

TS和JS的区别

  • TypeScript同JavaScript相比,最大的特点是强类型,支持静态和动态类型,和JavaScript不同,这种强类型相比弱类型,可以在编译期间发现并纠正错误,降低了试错的成本也提升了代码的规范性。

TS例子

  • 先说一下 JS 的现状:
  1. 在 JS 中的变量本身是没有类型,变量可以接受任意不同类型的值,同时可以访问任意属性,属性不存在无非是返回 undefined
  2. JS 也是有类型的,但是 JS 的类型是和值绑定的,是值的类型,用 typeof 判断变量类型其实是判断当前值的类型
// JavaScript

var a = 123
typeof a // "number"
a = 'sdf'
typeof a // "string"
a = { name: 'Tom' }
a = function () {
  return true
}
a.xxx // undefined
  • TS 做的事情就是给变量加上类型限制
  1. 限制在变量赋值的时候必须提供类型匹配的值
  2. 限制变量只能访问所绑定的类型中存在的属性和方法
  • 举个简单的例子,如下是一段能够正常执行的 JS 代码
let a = 100
if (a.length !== undefined) {
  console.log(a.length)
} else {
  console.log('no length')
}
  • 直接用 TS 来重写上面的代码,把变量 a 的类型设置为 number。在 TS 中给变量设置类型的语法是 【 : Type 】 类型注解
let a: number = 100
if (a.length !== undefined) { // error TS2339: Property 'length' does not exist on type 'number'.
  console.log(a.length)
} else {
  console.log('no length')
}

创建数组

  • TS创建数组的方式
let array1:Array<number>;
let array2:number[];
  • JS创建数组的方式
let array1 = new Array()
let array1 = []
  • TS创建的数组只允许存放相同类型的数据。
  • 例子
let character:string[] = ["杨过", "小龙女", "郭襄", "郭靖", "黄蓉", "李莫愁", 1];
console.log(character); // Type 'number' is not assignable to type 'string'.
  • TS创建对象类型的数组
interface IArrStudent{
    name:string,
    age:number
}
const arrType5:Array<IArrStudent>=[{ name:"Mr.A",age:18},{ name:"Mr.B",age:20}]

const arrType6:IArrStudent[]= [{ name:"Mr.A",age:18},{ name:"Mr.B",age:20}]
  • TS中如果需要一个数组需要存放两种数据类型,需要使用联合类型(string | number)[] demo:
var foo: (string|number)[] = [ 1, "message" ];
!和?
let y:number

y = null		// 无法通过编译
y = undefined	// 无法通过编译

y = null!
y = undefined!
interface IDemo {
    x?: number
}

let y:number

const demo = (parma: IDemo) => {
    y = parma.x || 1	// 如果为undefined,返回y=1,如果不为undefined,则返回parma.x的值
    return y
}

小知识

  • Typescript的作者是C#之父,C#和java的语法非常接近。所以TS比较容易被写java的人接受

extends,implements区别

  1. 类实现接口(支持多个接口实现)
// 动物的接口 
interface Animal {
  type: string;
  sound: string;
  voice():void;
}

// Dog类实现接口
class Dog implements Animal {
  type:string;
  sound: string;
    
  voice():void {
    console.log(`${this.sound}, 我是${this.type}`)
  }

  constructor(sound: string,type: string) { 
    this.sound = sound
    this.type = type
  }
}

// Cat类实现接口
class Cat implements Animal {
  type: string;
  sound: string;
    
  voice(): void {
    console.log(`${this.sound}, 我是${this.type}`)
  }

  constructor(sound:string, type: string) {
    this.sound = sound;
    this.type = type;
  }
}

new Cat("喵喵~","哺乳类").voice();
new Dog("汪汪~","哺乳类").voice();

结果:

喵喵~, 我是哺乳类
汪汪~, 我是哺乳类

2.接口继承接口(支持多继承)

// 生物体的接口
interface Creature  {
  name: string;
}

// 动物接口  继承生物接口
interface Animal extends Creature {
  // 自己拥有的属性 action
  action(): void;
}

class Dog implements Animal {
  name: string; // name是 Animal继承自 Creature的,不实现会报错
  action(): void {
    console.log(`我是${this.name}`)
  }

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

new Dog("狗狗").action()  // 我是狗狗
TIPS
  1. 类必须实现它的接口的所有属性,包括它继承自父类的属性
  2. 接口可以多继承:一个接口可以继承多个接口
// 生物体的接口
interface Creature {
  name: string;
}

// 动物接口  
interface Animal {
  // 自己拥有的属性 action
  action(): void;
}

// 狗Dog接口继承 生物Creature 和 Animal 多继承
interface Dog extends Creature, Animal{
  color: string;
}


class Golden implements Dog {
  name: string;
  color: string;
  action():void {
    console.log(`我是${this.name},我的颜色是${this.color}`)
  }

  constructor(name: string, color:string) {
    this.name = name;
    this.color = color;
  }
}

new Golden("金毛","金黄色").action() // 我是金毛,我的颜色是金黄色

Golden 实现了 Dog接口,Dog接口多继承了Creature 和 Animal两个接口,拥有了他们的属性,所以Golden要将他们全部实现。

  1. 类继承类(单继承)
// 父类
class Person {
  name: string;
  constructor(name: string) {
    this.name = name;
  }

  run(): string {
    return `${this.name}在奔跑`;
  }
}

let p = new Person('张三');
console.log(p.run());

// 子类Web继承父类Person
class Web extends Person {

  constructor(name: string) {
    super(name);   // 表示调用父类的构造函数
  }

}

let w = new Web('李四');
console.log(w.run());

面向对象的三大特点

  1. 继承

子类可以继承父类的protected,public的方法和属性,private是私有属性不能获取

  1. 封装

封装的意义

  • 封装的意义在于保护或者防止代码(数据)被我们无意中破坏。
  • 保护成员属性,不让类以外的程序直接访问和修改;
  • 隐藏方法细节
  1. 多态

多态的条件

  • 继承的存在(继承是多态的基础,没有继承就没有多态).
  • 子类重写父类的方法(多态下调用子类重写的方法).
  • 父类引用变量指向子类对象(子类到父类的类型转换).
/**
 * Animal 是一个抽象类,里面含有一个eat()抽象方法
 */
abstract class Animal{
    public name:string;
    constructor(name:string){
        this.name=name;
    }
 
    //抽象方法 ,不包含具体实现,要求子类中必须实现此方法
    abstract eat():any;
 
    //非抽象方法,无需要求子类实现、重写
    run(){
        console.log('非抽象方法,不要子类实现、重写');
    }
}
 
class  Dog extends Animal{
 
    //子类中必须实现父类抽象方法,否则ts编译报错
    eat(){
       return this.name+"吃肉";
    }
}
 
class Cat extends Animal{
 
    //子类中必须实现父类抽象方法,否则ts编译报错
    eat(){
        return this.name+"吃鱼";
    }
}
 
var dog =new Dog("tom");
var cat=new Cat("kitty");
console.log(dog.eat());
console.log(cat.eat());
 
//多态 ,一种事物的不同表现形态。如下面的代码中 先声明变量f是Animal类型,具体是Dog还是Cat,在new 对象时才知道
//如果是Dog,则f.eat()调用的是Dog类中的eat方法;如果是Cat,则f.eat()调用的是Cat类中的eat方法,这就是多态!!!
var f:Animal;//声明变量为Animal类型
//f=new Dog("sunny");
f=new Cat("sunny");
console.log(f.eat());
抽象类
  1. 抽象类不一定要有抽象方法
  2. 有抽象方法的一定是抽象类
  3. 父类有抽象方法,子类必须去实现抽象方法
  4. 抽象类只能作为基类,不能实例化的(不能new)
  5. 若子类继承抽象类,并重写了所有的抽象方法,则此类是一个”实体类”,即可以实例化
  6. 若子类继承抽象类,没有重写所有的抽象方法,意味着此类中仍有抽象方法,则此类必须声明为抽象的!
type 与 interface 的区别

官方文档

  • An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.
  • An interface can have multiple merged declarations, but a type alias for an object type literal cannot.

相似的

  • type 用于定义数据的类型别名。interface 用于定义数据的类型别名。
interface User {
  name: string
  age: number
}

interface SetUser {
  (name: string, age: number): void;
}
type User = {
  name: string
  age: number
};

type SetUser = (name: string, age: number)=> void;
  • 都允许拓展(extends)
interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
type Name = { 
  name: string; 
}
type User = Name & { age: number  };
type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}

不同点

  • type 可以声明基本类型别名,联合类型,元组等类型
  • interface只能定义对象类型。
// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
  • interface 可以而 type 不行
// interface 能够声明合并
interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string 
}
*/

结论

  • 一般来说,如果不清楚什么时候用interface/type,能用 interface 实现,就用 interface , 如果不能就用 type
  • 根据官方文档所说,如果构建的是公开第三方库的类型,还是建议使用interface.
  • interface 更倾向于继承;而 type 则更倾向于组合。