TypeScript 系统入门到项目实战--个人笔记

502 阅读7分钟

导学

1.Ts基础语法--2.爬虫功能开发--3.Ts语法进阶--4.项目接口开发---5.TS高级语法--6.项目代码重构--7.项目前端开发--8.总结

知识点:

静态对象 类型注解 类型推断 泛型

类型定义文件 模块化 打包编译 装饰器

Metadata 设计模式 实战问题 ....

前提:

Es6 and Js

Node expressLinkIcon

收获:

应对企业级别动TS项目

在框架层面有更高的理解

扩展技术视野

TypeScript 基础语法入门

TypeScript带来了什么优势

image.png 1.开发过程中,发现潜在问题

2.更友好的编辑器自动提示

3.代码语义更清晰易懂

TypeScript基础环境搭建

1.安装node,node -v 查看是否安装成功

2.npm -v 查看是否安装成功

安装ts > npm install -g typescript

node demo.ts 会报错,node无法直接调用ts文件,只能用tsc demo.ts将ts编译生成js 再用node demo.js去执行js文件

// demo.ts
interface Point { x: number, y: number }

function tsDemo(data: Point) {
  return Math.sqrt(data.x ** 2 + data.y ** 2);
}

tsDemo({ x: 1, y: 123 });

image.png

image.png 为了简化这个ts转js 再执行js的这个过程,可以安装ts-node

安装命令:npm install -g ts-node

安装完成后,直接用ts-node demo.ts 即可直接执行demo.ts 不用额外编译生成js

image.png

静态类型的深度理解

定义一个静态类型 不仅仅意味着这个变量的类型不能修改,也意味着这个变量的属性 和方法基本也就确定了。

基础类型和对象类型

对基本类型 、对象类型、函数、对象、数组 示例:

可参考掘金上链接学习ts对类型的处理:juejin.cn/post/695005…

image.png

函数:返回的是个数字类型的,所以在这里需要标注是number类型,当return如果是string类型或者其他类型就会有提示。

image.png

类型注解和类型推断

基本类型相加减 一般会自动分析出类型来,当函数,需要给出类型,具体如下所示:

// type annotation 类型注解, 我们来告诉 TS 变量是什么类型
// type inference 类型推断, TS 会自动的去尝试分析变量的类型
// 如果 TS 能够自动分析变量类型,我们就什么也不需要做了
// 如果 TS 无法分析变量类型的话,我们就需要使用类型注解

// let count: number;
// count = 123;

// let countInference = 123;

// const firstNumber = 1;
// const secondNumber = 2;
// const total = firstNumber + secondNumber;

function getTotal(firstNumber: number, secondNumber: number) {
  return firstNumber + secondNumber;
}

const total = getTotal(1, 2);

const obj = {
  name: 'dell',
  age: 18
};

函数相关类型

函数未必返回我们需要的类型,所以最好也给定义个类型,如下图 如果在return中加上 return first + srcond + '‘,这样 total返回的就是string类型。

在函数add处规范了number类型,如果返回的是string类型,就会有提示。

image.png

如果类型是void,那么后面就不应该有返回值

下面这样是可以的,但是如果花括号里加了return就会提示

function sayHello():void{

console.log('hello')

}


function sayHello():void{

    return 123 // 会报错

}

如果后面压根就不会执行,那么类型是never。示例如下面代码:

function errorEmitter():nerer{

throw new Error();

console.log(123)// 永远不会执行至此

}

function errorEmitter():nerer{

while(true){}

console.log(123)// 永远不会执行至此

}

解构的语法 如果ts给出类型(后面跟着类型注解 只要是解构,就必须写在后面的花括号里):示例如下

image.png

function add({ first, second }: { first: number; second: number }): number {
  return first + second;
}


const total = add({ first: 1, second: 2 });

基础语法复习

// 基础类型, boolean, number, string, void, undfined, symbol, null

// let count=123; 这时候count能类型推断出是number类型,


// 当 let count;
// count=123; 放在两行的时候,无法准确推断,这时候 let count会将其推断成any类型

let count: number;
count = 123;

// 对象类型, {}, Class, function, []
// 下面两种方式一样:
// 第一种写法 类型注解 会推断出,返回值就是number类型,所以 func函数 可以不用去写number返回值
const func = (str: string) => {
  return parseInt(str, 10);
};

const func1: (str: string) => number = str => {
  return parseInt(str, 10);
};


const date = new Date();

// 其他的 case
interface Person {
  name: 'string';
}
const rawData = '{"name": "dell"}';
const newData: Person = JSON.parse(rawData);// JSON.parse 不能推断出newData的类型,可以在上面定义个interface


// 如果一个变量可能是number也有可能是string类型,可以使用或
let temp: number | string = 123;
temp = '456';

数组和元组

// 数组
const arr: (number | string)[] = [1, '2', 3];// 每一项既可以是number类型也可以是string类型
const stringArr: string[] = ['a', 'b', 'c'];// 每一项都是string类型
const undefinedArr: undefined[] = [undefined];// 每一项都是undefined,如果不是就会报错

// type alias 类型别名

type User={name:string,age?:number} 类型别名

const objectArr:User[]=[{ // objectArr存储的每一项必须是User类型,

name:'dell',age:28

}]


type User = { name: string; age: number };

class Teacher {
  name: string;
  age: number;
}

const objectArr: Teacher[] = [
  new Teacher(),
  {
    name: 'dell',
    age: 28
  }
];




// 元组 tuple 数组中长度是固定的,每一项的类型也是固定的,可以用元组
const teacherInfo: [string, string, number] = ['Dell', 'male', 18];

// csv
const teacherList: [string, string, number][] = [['dell', 'male', 19], ['sun', 'female', 26], ['jeny', 'female', 38]];

像是csv/excel这种导出的文件,转换为js时候, 可以考虑用元组来进行管理。

interface 接口

有通用性的 可以抽离出 interface

interface 和 type(类型别名)有什么区别?

juejin.cn/post/684490…

如果能用interface表述的话就用interface,实在不行 就用type类型别名

当变量声明类型前 加了 readonly,那么这个变量就不能再改写了

image.png

// interface 和 type 相类似,但并不完全一致
interface Person {
  // readonly name: string;
  name: string;
  age?: number;
  [propName: string]: any;
  say(): string;
}


// 老师继承了person的所有属性,再加个teach
interface Teacher extends Person {
  teach(): string;
}


// interface 定义函数类型
interface SayHi {
  (word: string): string;
}

const getPersonName = (person: Person): void => {
  console.log(person.name);
};

const setPersonName = (person: Teacher, name: string): void => {
  person.name = name;
};

const person = {
  name: 'dell',
  sex: 'male',
  say() {
    return 'say hello';
  },
  teach() { // Teacher 需要有teach方法
    return 'teach';
  }
};

getPersonName(person);
setPersonName(person, 'lee');


// 类想去应用(implements)接口,需要满足接口的要求,否则会报错
class User implements Person {
  name = 'dell';
  say() {
    return 'hello';
  }
}

const say: SayHi = (word: string) => {
  return word;
};


类的定义与继承

class Person {
  name = 'dell';
  getName() {
    return this.name;
  }
const person = new Person();
console.log(person.getName()); 
// ts-node demo.ts
// 输出:dell

类的继承,被继承的叫父类 ,继承的 叫 子类。下面 teacher是子类,Person是父类。子类可以对父类的方法进行改写。

class Person {
  name = 'dell';
  getName() {
    return this.name;
  }
}

class Teacher extends Person {
  getTeacherName() {
    return 'Teacher';
  }
  // getName() {
  //   return  'lee';
  // }
  
  getName() {
    return super.getName() + 'lee'; // super相当于Person
  }
}

const teacher = new Teacher();
console.log(teacher.getName());
console.log(teacher.getTeacherName());

// ts-node demo.ts 
// 输出:
// lee 
// Teacher


 ts-node demo.ts 
// 输出:
 dell lee 
 Teacher

面试:super在编写代码的时候一般用它干嘛?回答出:当我一个类把父类的方法覆盖掉的时候(重写了之后),如果这个时候还想调用父类的方法,那就可以通过super调用父类的方法。这就是super比较常见的一个场景。

类中的访问类型和构造器

private 、protected 、public

// private, protected, public 访问类型
// public 允许我在类的内外被调用
// private 允许在类内被使用
// protected 允许在类内及继承的子类中使用
class Person {
  public name: string; // 如果这里改为private name:string;类外调用this.name就会报错
  public sayHi() {
    this.name;
    console.log('hi');
  }
  private sayABC() {
    this.name;
  }
}

class Teacher extends Person {
  public sayBye() {
    this.name(); // 如果上面改为private name:string;这里调用this.name就会报错
  }
}
class Person { // 与上面的class Person一样
  public name: string; 
  public sayHi() {
    this.name;
    console.log('hi');
  }
  private sayABC() {
    this.name;
  }
}


const person = new Person();
person.name = 'dell';
console.log(person.name);
person.sayHi();

// constructor
class Person {
  // 传统写法
  // public name: string;
  // constructor(name: string) {
  //   this.name = name;
  // }
// ---下面是上面四行的简写---
  // 简化写法
  constructor(public name: string) {}
}
const person = new Person('dell');
console.log(person.name);
class Person {
  constructor(public name: string) {}
}

class Teacher extends Person {
  constructor(public age: number) {
    super("法外狂徒"); // 如果不传入name会报错
  }
}

const teacher = new Teacher(28); //对应Teacher的age
console.log(teacher.age);
console.log(teacher.name);

// ts-node demo.ts
// 输出:
// 30
// 法外狂徒

静态属性,Setter和Getter

单例模式---静态属性是放在类上的,另外理解单例模式

class Demo {

private static instance: Demo;

private constructor(public name: string) {}

static getInstance() {

if (!this.instance) {

this.instance = new Demo("dell lee");

}

return this.instance;

}

}

const demo1 = Demo.getInstance();

const demo2 = Demo.getInstance();

console.log(demo1.name);

console.log(demo2.name);

抽象类

抽象类只能被继承,不能被实例化

// 抽象类

abstract class Geom {

width: number;

getType() {

return 'Gemo';

}

abstract getArea(): number;

}

new Geom 是不可以的。

下面这样继承是可以的。

class Circle extends Geom {

getArea() {

return 123;

}

}