typescript学习笔记

298 阅读9分钟

TypeScript是JavaScript的超集,主要提供类型系统和对ES6的支持

使用TypeScript好处

  • 增加代码可读性和我可维护性,在编译阶段发现问题,而不是运行时
  • typescript具有包容性,js文件可以直接更名为ts文件,编译报错也能生成js文件
  • 社区活跃,兼容第三方库,支持ES6规范

安装

npm install -g typescript

编译

tsc hello.ts

初始化Ts项目

tsc --init

基础

原始数据类型

布尔值

let isDone:boolean = false;

字符串

let str:string = 'hello';

数字

let num:number = 123;

空值

可以用 void 表示没有任何返回值的函数

function alertName(): void {
    alert('My name is Tom');
}

null和undefined

let u: undefined = undefined;
let n: null = null;

任意值

any类型的变量,允许被赋值为任意类型

let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;

任意值上允许访问任何属性,调用任何方法

let anyThing: any = 'hello';
console.log(anyThing.myName);
anyThing.setName('Jerry');

声名变量未指定其类型,它会被识别为任意值类型

let something 等价于 let something:any;

类型推断

定义变量未指定类型,会根据赋值推断出一个类型

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7; 
//报错

但是如果声明类型未赋值,会被推断成any任意类型

联合类型

联合类型表示可以是多种类型中的一种,使用 | 分隔,

只能访问此联合类型的所有类型里共有的属性或方法

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

数组类型

  • 类型+[]表示法
let arr:number[]=[1,2,3,4];
  • 数组泛型表示法
let arr:Array<number>=[1,2,3,4];
  • 元组,数组每一项固定
let arr:[string,number] = [1,2];
  • 接口表示
interface myArr {
[index:number]:number
}

函数类型

  • 函数声明
function sum(x: number, y: number): number {
    return x + y;
}

  • 函数表达式

在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};
  • 接口定义函数
interface myFun{
(param1:string,param2:string):boolean
}

let fun:myFun;
fun = function(param1:string,param2:string){
return param1 === param2
}
  • 解构语法类型注解
function fn1({ first, last }: { first: number, last: number }): number {
    return first + last;
}
  • 可选参数

可选参数必须在必须参数后面

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
  • 默认参数

TypeScript 会将添加了默认值的参数识别为可选参数,不受「可选参数必须接在必需参数后面」的限制了

function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
  • 剩余参数
function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}

  • 重载

TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。

function reverse(x: number): number;
function reverse(x: string): string;

类型别名type

类型别名用于给类型起一个新的名字,比如:

type myStr = string
let str:myStr = 'fsdfds'

类型别名多用于联合类型

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

字符串字面量类型, 限定只能是几个字符串中的一个

type EventNames = 'click' | 'scroll' | 'mousemove';

枚举类型

用于取值被限定在某一个范围内的场景

  • 如果枚举未赋值,默认值从0开始,手动给某一项赋值,前面还是从0开始,后面递增1
enum Status{
    offLine,
    onLine
}
console.log(Status.offLine);//0
console.log(Status.offLine);//1
  • 枚举可以部分赋值,未赋值的部分在上一个赋值的基础上+1
enum Status{
    offLine=1,
    onLine
}
console.log(Status.offLine);//1
console.log(Status.offLine);//2
  • 枚举可以是小数或者负数,未赋值的部分在上一个赋值的基础上+1
enum Status{
    offLine=1.5,
    onLine
}
console.log(Status.offLine);//1.5
console.log(Status.offLine);//2.5

类与继承

ES6类的基本使用

  • 通过class关键字声明类,类的名字大写
  • constructor是构造方法,内部的this指向实例对象,里面声明的属性是实例属性
  • 类上声明的函数,不需要funtion,也不需要逗号分隔,是类原型上的方法
  • 通过new关键字实例化一个对象
class Person {
  constructor(name) {
    this.name = name;
  }

  play() {
    console.log("play");
  }
}
let person = new Person("lxr");

上面这段代码,用ES5实现

function Person(name){
    this.name = name
}

Person.prototype.play = function (){
    console.log('play')
}

可以看出,ES6 class其实就是ES5 构造函数的语法糖,但是二者还是有一些不同的地方:

  • class类只能通过new调用,不能作为普通函数
  • class类没有声明提前,只能先声明,再使用
  • class类上声明的函数,都是不可枚举的

constructor

constructor是实例构造器,作为类的默认方法,如果不写会默认添加,在new的时候自动执行

class Point {
}
// 等同于
class Point {
  constructor() {}
}

constructor默认返回this,指向实例对象,如果指定返回其他对象,那new出来的实例就不是类的实例

class Point {
  constructor() {
  return {name:'lxr'}
  }
}
let point = new Point()//{name:lxr}

类的实例

  • constructor中定义在this上的属性,是实例上的属性,除此之外都在原型上
class Person {
  constructor(name) {
    this.name = name;
  }

  play() {
    console.log("play");
  }
}
let person = new Person("lxr"); 
person.hasOwnProperty('name');//true
person.hasOwnProperty('play');//false
person.__proto__.hasOwnProperty('play') // true

  • 类的实例跟es5的实例一样,共享一个原型对象
class Person {
  constructor(name) {
    this.name = name;
  }

  play() {
    console.log("play");
  }
}
let person1 = new Person("lxr"); 
let person2 = new Person("zpd"); 
console.log(person1.__proto__ === person2.__proto__);//true

取值函数(getter)和存值函数(setter)

在“类”的内部可以使用get和set关键字,对某个属性设置存值函数和取值函数,拦截该属性的存取行为。

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
  get fullName() {
    console.log("get");
    return this.firstName + this.lastName;
  }
  set fullName(val) {
    console.log("set", val);
  }
}
let person = new Person("l", "xr");
console.log(person.fullName); //get lxr
person.fullName = "zpd";//set zpd

注意点

  • 不存在声明提前
  • 默认严格模式
  • name属性总是返回紧跟在class关键字后面的类名
  • 类的方法内部如果含有this,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。
class Person {
  constructor(name) {
    this.name = name;
  }

  play() {
    console.log(this.name);
  }
}
let person = new Person("lxr");
const { play } = person;
play();

上面的代码会报错,因为play方法独立调用,class采用严格模式,所以 this 实际指向的是undefined,解决方案如下:

//提前绑定this
constructor(){
	this.play = this.play.bind(this);
}

静态方法

类相当于实例的原型,类中的方法都会被实例继承,在方法前面加上static关键字,那么这个方法就不会被实例继承,通过类自身直接调用,称作静态方法,

class Person {
  static walk() {
    console.log("walk");
  }
}
Person.walk();//  Person walk

如果静态方法中存在this,那么this指向类,而不是实例

class Person {

  static walk() {
    console.log(this)
  }
}
Person.walk();//  Person 

静态方法可以被子类继承

class Foo {
  static classMethod() {
    return 'hello';
  }
}

class Bar extends Foo {
}

Bar.classMethod() // 'hello'

子类可以通过super调用父类静态方法

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

  static walk() {
    console.log(this);
  }
}

class Sub extends Person {
  static walk() {
    super.walk();
  }
}

Sub.walk();

class继承

class通过extends字段实现继承

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

class Sub extends Person {
}

上面代码中的子类继承了父类,什么也没做,所以相当于复制了一个父类

子类的构造函数必须执行一次super函数


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

class Sub extends Person {
  constructor(name, age) {
    super(name);
    this.age = age;
  }
}

let sub = new Sub("lxr", 18);


TS中的类

ES6中,类的实例属性只能在构造器通过this.XXX= XXX声明,Ts中,类的实例属性可以直接在类里面定义

class Animal {
  name = 'Jack';

  constructor() {
    // ...
  }
}

let a = new Animal();
console.log(a.name); // Jack

ES6中,只能定义静态方法,在ts中可以定义静态属性

class Animal {
  static num = 42;

  constructor() {
    // ...
  }
}
console.log(Animal.num); // 42

publicprivateprotected修饰符

  • public定义的方法和属性都是共有的,所有属性和方法默认都是public
class Person{
    public name;
    public constructor(name){
        this.name = name
    }
}
let person = new Person('lxr')
console.log(person.name)//lxr

  • private定义的属性和方法都是私有的,不能在类的外面访问,也不能被子类访问, 构造器使用private,该类不能被继承和实例化 - protected定义的属性和方法,不能在类的外面访问,但是能在子类中访问,构造器使用protected,该类只能被继承,不能被实例化

修饰符可以在参数中使用

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

等价于

class Animal {
  public constructor(public name) {
  }
}

接口

接口可以用于对「对象的形状(Shape)」进行描述,接口的另一个用途,对类的一部分行为进行抽象

定义对象类型

  • 用接口声明对象,形状必须保持一致
interface Person{
    name: string;
    age: number;
}

let person: Person = {
    name: 'tom',
    age:18,
}
  • 表示可选属性
interface Person{
    name: string;
    age: number;
    height?:number
}

let person: Person = {
    name: 'tom',
    age:18,
}
  • readonly表示只读属性
interface Person{
    name: string;
    age: number;
    readonly sex: number
}

let person: Person = {
    name: 'tom',
    age:18,
    sex:1
}
person.sex = 0//报错
  • 任意属性 !注意:设置任意属性时,其他属性的类型,必须是任意类型的子类型
interface Person{
    name: string;
    age: number;
    [prop:string]:any
}

let person: Person = {
    name: 'tom',
    age:18,
    county:'中国'
}

对类进行抽象

  • 类实现接口,把类的共有特性,提取成接口,通过implements实现
interface action {
    run(): void,
    jump(): void
} 

class Person implements action{

    run(){
        console.log('run')
    }
    jump(){
        console.log('jump')
    }
}
let person = new Person()
person.run()

  • 接口继承接口
interface Alarm {
    alert(): void;
}

interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
  • 接口继承类
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};

泛型

是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

  • 函数泛型
定义函数时泛指一个类型,调用时指定具体类型

function join<ABC>(first:ABC,second:ABC){
return `${first}${second}`
}

join<number,string>(1,'1');

  • 数组泛型
let arr:Array<number>=[1,2,3,4]

  • 类中的泛型
class Person<T>{
    constructor(private data:T){

    }
}
const person = new Person<string>('lxr')

```typescript
- 使用泛型作为具体的类型注解
```typescript
function hello<T>(params:T){
    return params;
}

cosnt func:<T>(params:T)=>T=hello;

类型定义文件编写

在.d.ts文件中写类型定义文件

//定义全局变量
declare var $:(params:()=>void)=>void;

//定义全局函数,可以定义参数不同,函数名相同的函数(函数重载)
declare function $(params:()=>void):void;

declare function $(params:string):{html:(html:string)=>{}}

//定义对象,命名空间嵌套,定义类
declare namespace ${
    namespace fn{
        class init{
            
        }
    }
}

$.fn.init();

//接口语法
interface JQuery{
    (Func:()=>void):void;
    (selector:string):{html:(parans:string)=>{}}
}
var $:JQuery;

类的装饰器

  • 对类进行修饰的函数,接收构造函数
  • 通过@符号引用
  • 类定义时候进行修饰,不是实例化
function testDecorator(){
    //返回一个装饰器函数
    return function<T extends new (...args:any[])=>{}>(constructor:T){
        return class extends constructor{
            name = 'L';
            getName(){
                return this.name;
            }
        };  
    }
}

// @testDecorator
const Test = testDecorator()(
    class {
        name:string;
        constructor(name:string){
            this.name = name;
        }
        }
)

const test = new Test('lxr')
console.log(test.getName())