TS 学习笔记
- 学习Ts时做的笔记,偏重ts中的基础使用和区别于js的特性,实践案例将来遇到将会补充进来
不得不说,和Java太像了。。。有Java基础的学起来不要太舒服
- 看这篇文章的小伙伴需要有一定的js基础
文章目录
一. TS概述
1. 什么是TS
- TypeScript 简称TS
- ts是js的超集,最终被编译为js代码执行
- ts扩展了js的语法,现有的任何js程序都可以运行在ts环境中
- ts有强大的类型系统,强类型语言
二. Vscode中如何写ts代码
1.手动编译
- ts中如果使用了ts独有的语法的话,需要手动编译成js才能在浏览器中运行,否则不需要单独编译
- 编译方法:tsc+相对路径
tsc ./01.ts
之后在html中引用编译出来的js即可
- 这个例子中ts和编译出的js区别
2. 自动编译
- 首先在cmd中输入
tsc --init
- 生成一个json文件,改动里面的outDir
- 之后再vscode的 终端——运行任务——所有任务——tsc监视打开自动编译
- 效果:保存后会自动生成js文件
三.基本类型(最基本的几个基本类型,其他基本类型展开说)
- 定义方式
let 变量名:类型= 对应类型的值
- 布尔类型
- 数字类型
- 字符串类型
let flag:boolean=true; //布尔值类型
let num:number=1; //数字类型
let str:string="szk";//字符串类型
- undefined和null:
- 两者都表示”空“,但又有所不同
- undefined表示应该有值,但是还没被赋予
- null表示这里就应该为空
- 两者可以认为是所有类型的子类
let und:undefined=undefined;
let null:null=null;
//两者本身也是数据类型
let num1:number=undefined;
number=null;
四. 数组和元组
1. 数组
- 数组中的元素类型是一开始指定好的,是唯一的
- 数组的两种声明方式
// 第一种方式:let 变量名:类型[]
let arr1:number[]=[1,2,3,4,5];
// 第二种方式:泛型写法 let 变量名:<类型>
let arr2:Array<number>=[1,2,3,4,5]
2.元组
- 元组中元素可以是不同的类型,类型和个数都要事声明好
//元组
let arr3:[string,number,boolean]=["szk",99,true];
五. 枚举
- 枚举类型中的每个数据值都叫做元素,元素的值从0开始递增,也可以手动指定值
- 如果手动指定的值是数字类型,那下一个就在当前基础上+1,如例子中的black
- 如果手动指定的是字符串类型,则后面的元素必须要指定字符串类型的值,不然就会报错
// 枚举类型中的每个数据值都叫做元素,元素的值从0开始递增,也可以手动指定值的大小
enum Color{
red,
white=100,
black,
pink='szk'
}
let red:Color=Color.red;
let black:Color=Color.black;
console.log(red); // 结果是0
console.log(Color.white)// 结果是100
console.log(black); //结果是101
console.log(Color.pink);//结果是szk
console.log(Color['szk']) //结果是pink
- 通过值获得名字
六. any和void类型
- 不确定数据是什么类型的时候,又想存储任意类型,就用any
let a:any = 'szk'
a=100
- 不确定数组中的数量个数,和数据类型可以使用any
let arr:any[]=[100,"szk"]
- 缺点:不会有报错信息,就没有ts的优势了
- 函数没有返回值,用void
function fun1():void{
console.log('123')
}
七. 联合类型和类型断言
- 让传入的参数可以是多种类型
- 写法: function fun2(parms:string|number) 使用" | "分隔不同类型
- 这种方法传参进去,在静态代码的时候编译器不知道你在使用的是什么类型,可能会报错,这时候需要类型断言,告诉编译器我知道我在用什么类型,我知道我在干什么
- 类型断言的写法:
- <类型>变量名 如: < number > parms 就指定了这个地方的parms是个number类型
- 变量名 as 类型 如: parms as string
function fun2(parms:string|number):number{
//如果传进来是个字符串,使用类型断言,告诉编译器“我知道我在做什么”,不会类型报错
if((<string>parms).length){
return (<string>parms).length
}else{
return (<number>parms).toString.length
}
}
八. 接口interface
- 接口:一种约束,可以约束一个对象中需要含有的属性
1. 接口写法
interface iPeople{
name: string,
//属性名:属性类型
age:number
}
- 在函数中可以让参数为interface声明的类型
// 传的person必须满足iPerson接口,否则报错
function getPerson(person:iPerson){
console.log(person.name+"-"+person.age)
}
- 传入的person必须满足接口要求的属性的类型
let person={
name:"szk",
age:99
}
2. 只读类型和可选类型
- 在接口中可以声明属性是只读类型或是可选类型
- 只读类型:加一个readonly
- 可选类型: 属性名之后加一个?
interface IPerson{
name:string,
readonly age:number, //只读
sex?:string //可选属性
}
// 传的person必须满足iPerson接口,否则报错
function getPerson(person:IPerson){
console.log(person.name+"-"+person.age)
}
let person:IPerson={
name:"szk",
age:99
}
3. 用来约束函数
- 在接口中定义一个“签名”,可以用来规定函数类型,说人话就是函数中的传参数量,传参名称和返回值都可以在接口中事先约束
interface IFunction{
//在接口写一个签名,就是实现这个接口的函数需要满足的传参规则
(firstName:string,lastName:string):string;
}
//用这种方式实现这个接口,function后面的内容需要跟接口中的一样
const fun1:IFunction=function(firstName:string,lastName:string):string{
return firstName+lastName;
}
- 完整案例
(()=>{
//声明类一个接口约束iPerson
interface iPerson{
name:string,
age:number
}
// 传的person必须满足iPerson接口,否则报错
function getPerson(person:iPerson){
console.log(person.name+"-"+person.age)
}
let person={
name:"szk",
age:99
}
getPerson(person)
})()
4.接口的继承
- 接口通过
extends
关键字可以继承别的多个接口
interface ITeacher{
teach();
}
interface IBoss{
getMoney();
}
interface IPeople extends ITeacher,IBoss{
//通过extends关键字可以继承多个接口
}
九. 类class
- 类:可以理解为模板,通过模板可以实例化对象
1. class的组成
- 属性:这个类中的“数据”
- 构造器:new这个类需要提供什么参数来实例化
- 方法:类里面提供方法
2. 一个例子就明白
//定义Person类
class Person{
//属性
firstName:string;
lastName:string;
//有参构造
constructor(firstName:string,lastName:string){
this.firstName=firstName;
this.lastName=lastName;
}
//方法的定义
speek(word:string) {
console.log(word);
}
}
//
let person=new Person("s","zk"); //通过new关键字有参构造了一个person实例
//一个强制参数类型是Person的函数
function getPerson(person:Person){
console.log(person.firstName+person.lastName);
}
//可以通过“.“ 调用类中的方法
person.speek("hello");
3. 实现接口
- 类实现接口使用
implements
关键字,接口中定义的方法必须在类中重写,不然会报错
///
//定义了一个接口,里面有一个方法
interface ITeach{
teachStudent(teacher:string,studentNum:number):string
}
//用implemes继承了这个接口,必须要实现里面的teachStudent方法
class School implements ITeach{
teachStudent(teacher: string, studentNum: number):string {
return (teacher+"teach"+studentNum.toString)
}
}
- 实现继承了多个接口的接口时,要把子接口的方法全部实现
//接口的继承
interface ITeacher{
teach();
}
interface IBoss{
getMoney();
}
interface IPeople extends ITeacher,IBoss{
//通过extends关键字可以继承多个接口
}
//类实现这个大接口,需要把大接口继承的接口的方法全部实现
class People implements IPeople{
teach() {
}
getMoney() {
}
}
4.类的继承
- 有了继承关系,就有了父子类的关系
- 可以继承的东西有:
- 构造器
- 方法
- 属性
class Person{
name:string;
age:number;
constructor(name:string,age:number){
this.age=age;
this.name=name;
}
sayHi(word:string):string {
console.log(word);
return word;
}
}
//子类Student继承了父类Person
class Student extends Person{
//使用super关键字调用父类的构造器
constructor(name:string,age:number){
super(name,age)
}
//重写父类的方法
sayHi(stuId:string): string {
return stuId
//super可以调用父类的方法
super.sayHi('你好')
}
}
let person=new Person("szk",18);
person.sayHi("hello");
let student=new Student("szzk",20)
student.sayHi("hello")
5. 修饰符
- 三个修饰符
- public :成员可以被类外部访问到
- private: 只有在当前类中可以访问
- protected:只能在当前类和其子类中可以访问,外部不行
- static :静态的
6. static
- 用static修饰的就是静态的
静态属性
- 用static修饰属性变量
- 调用时不用实例化,直接 类名.属性即可
class Person{
name:string;
age:number;
//static修饰的是静态属性
static job:string;
getJob(){
//调用类中的静态属性使用:类名.属性的方式调用
Person.job="teacher"
console.log
}
}
//外部调用静态属性使用:类名.属性的方式调用,不用实例化对象
console,log(Person.job)
静态方法
- 用static修饰方法名
- 调用静态方法通过:类名.方法名调用
class Person{
name:string;
age:number;
//static修饰的是静态属性
static job:string;
constructor(name:string,age:number){
this.age=age;
this.name=name;
}
//static修饰的方法叫静态方法
static getJob(){
//调用类中的静态属性使用:类名.属性的方式调用
Person.job="teacher"
console.log
}
}
//调用静态方法通过:类名.方法名调用
Person.getJbo();
7. 抽象类
- 抽象类不能被实例化
- 抽象类中可以包含
抽象方法和抽象属性
,也可以包含实例方法 - 使用时要别的类来继承,重写里面的抽象方法
abstract class Animal{
//抽象属性
abstract name:string;
//抽象方法,不能有具体的实现
abstract eat();
//可以有实例方法
sayHi(){
console.log("hi")
}
}
class Cat extends Animal{
// 实现抽象属性
name: string="stichi"
// 实现抽象方法
eat() {
console.log("eat")
}
}
抽象类和接口的异同
- 抽象类和接口都可以提供一种规范
- 抽象类里面可以有构造器,可以有实体方法,实体属性,而接口没有
- 抽象类更像是一种模板,子类中
有新内容就新增,但是抽象类中的内容也全部包含
- 接口一般只作为
行为的抽象,不能为类提供一个模板
- 具体可以参考这篇文章
(十)函数
1. ts中函数的写法
- 第一种写法:function 函数名(参数:参数类型,…):返回值类型
- 第二种写法:const 函数名=function(参数:参数类型,…):返回值类型
//第一种
function add1(a:number,b:number):number{
return a+b;
}
add1(1,2)
//第二种
const add2=function(a:number,b:number){
return a+b;
}
add2(2,3)
2.可选参数和默认参数
- 参数在声明时指定了值,就是默认参数
- 用
?
修饰的参数就是可选参数 - 例子中badGuy用?修饰,可选参数,下方调用的时候可以不填
function watchJOJO(season:number=1,mainCharacter:string="空调承太郎",badGuy?:string){
if(badGuy){
console.log(mainCharacter+season+badGuy)
}
else{
console.log(mainCharacter+season+"卡兹")
}
}
watchJOJO(3,"空调","吉良吉影");
watchJOJO(3,"空调");
3. 剩余参数
- 用于有不确定数量的参数
- 用
...args:string[]
,且放在参数的最后,来表示剩余参数 - 剩余参数以数组的形势出现
function restFun(a:string,b:string,...args:string[]){
console.log(a);
console.log(b);
console.log(args);
}
restFun("a","b","c",'d',"e","f")
4.函数重载
- ts中的函数重载和java中有区别,还挺大的
- 函数重载就是函数名相同,但接收的参数是不同的,称为函数的重载
- 函数重载,同名函数可以有多种接收方式,但是只能有一个函数最终实现
function fun1(a:string,b:string):string
function fun1(a:number,b:number):number
function fun1(a:any,b:any):any{
return a+b.toString;
}
//都叫fun1,但是接受的参数不同
- 为什么不用联合类型呢? 因为联合类型有如果对于可能输入数字或者字符串有四种组合,需要进行繁杂的判断,如下
function add2(a:number|string,b:number|string){ //如果只是联合类型,需要进行十分繁杂的判断
if(typeof a==='string'&& typeof b==='string'){
return a+b
}
}
- 如果使用的方法重载就可以限定两种组合
(十一)泛型
- 方法,接口,类在定义的时候
不能预先知道需要用的数据类型,而是在使用时才确定
,这个时候就要用到泛型 - 使用方式:在方法,接口,类名字后加上< T > ,然后在代码块中使用T代替类型
- 在调用的时候用同样的方法确定T
//通过 函数名<T> 这个T就表示泛型,后面的类型都可以用T先来代替,使用时会确定这个类型
//用泛型来适应在定义的时候不知道应该用什么类型,而在使用时才确定下来
function getArr<T>(value:T,count:number):T[]{
const arr:T[]=[];
for(let i=0;i<count;i++){
arr.push(value);
}
return arr;
}
//在调用的时候也用同样的方式确定类型
getArr<string>("abc",10);
2.泛型类
在定义类的时候设置泛型
class Animal<T>{
name:T
callHim(parm:T):T{
return parm ;
}
}
let dog:Animal<string>=new Animal();
dog.callHim("hhh")