Typescript基础知识梳理
之前一直打算看看ts,但一直沉迷摸 无法自拔,公司目前项目中也没有ts项目。今年手上正好有两个小程序项目,于是打算用ts写一下,感受一下ts的“魅力”。安装与编译安装顺便整理了一下学习笔记,希望能帮到有需要的人。
同时也用XMind做了知识图谱,有需要源文件的可以留言。最后!记得点赞+关注支持一下。感谢支持 。
npm install -g typescript //mac下安装前面需要加sudo
编译- 可以执行下面命令进行编译,会在当前目录下产生一个
test.js
文件
tsc test.ts
- 这样可能还是比较麻烦,我们可以借助插件方便我们提高效率:
npm install -g ts-node
- 安装完后只需执行
ts-node test.ts
就可以在控制台查看输出结果。
- js中数据类型分两种,原始数据类型和对象类型,原始类型包括:布尔值、数字、字符串、null、undefined以及Symbol。
- 在Typescript中,使用boolean定义布尔值类型
let isStatus:boolean=true;
数字- 使用number定义数值类型
let isNumber:number=1;//16进制let isNumber16:number:=0b1010;
字符串- 使用string定义字符串类型:
let isString:string='hello word';let year:number=2020;let add:string=`${isString},${year}!`;
空值- Javascript 没有空值(Void)的概念,在Typescript中,可以用void表示没有任何返回值的函数.
function alertName:void{ alert}
- 声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null。
let unusable: void = undefined;
Null 和 Undefined- 在Typescript中可以使用null和undefined来定义这两个原始数据类型
let u:undefined=undefined;let n:null=null;
- 与 void 的区别是,undefined 和 null 是所有类型的子类型。
也就是说undefined类型的变量,可以赋值给number类型的变量,而void类型的变量不能赋值给number类型的变量:
let num:number=undefined;//不会报错let u:undefined;let num: number = u;//也不会报错let u:void;let num:number=u;// Type 'void' is not assignable to type 'number'.
任意值- 任意值(Any)用来表示允许赋值为任意类型。
- 如果是一个普通类型,在赋值过程中改变类型是不被允许的:
let renyiString:string='string';renyiString=8;//Type 'number' is not assignable to type 'string'.
- 但如果类型是Any,类型,则允许被赋值为任意类型:
let renyiString:any='string';renyiString=8;
- 在任意值上访问任何属性都是允许的:
let anyThis:any='hello';console.logconsole.log
- 也允许调用任何方法:
let anyThing: any = 'Tom';anyThing.setName;anyThing.setName.sayHello;anyThing.myName.setFirstName;
- 可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
- 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:
let something;something = 'seven';something = 7;something.setName;
- 等于
let something: any;something = 'seven';something = 7;something.setName;
数组的类型基础表示- 「类型 + 方括号」表示法
let numbers:number[]=[1,2,3,4,5]
- 此时不允许出现其他类型,而且如果使用数组中push等方法,添加元素也得符合相应类型。
- 我们也可以使用数组泛型(Array Generic) Array
来表示数组
let fibonacci:Array=[1,2,3,4,]
- 如果数组中又有number类型又有string类型,则可以用|符号来区别定义:
let arr:[]=['tom',1];
数组中对象类型的定义- 项目中经常遇到数组中有对象的存在,对于这种就比较麻烦了,比如:
let arr:{name:string,age:number}[]=[{name:'tom',age:18}]
- 这样就比较麻烦了,我们可以使用ts中的类型别名来解决这个问题:
type PeopleType={name:String,age:Number};let arr:PeopleType[]=[ {name:'bob',age:19}]
- 也可以用类型定义也可以解决:
class PeopleType{ name:string; age:number}let arr:PeopleType[]=[ {name:'bob',age:19}]
元组的使用和类型约束- 在数组中如果里面又有string和number,可以使用|来进行定义,但一定程度上并不严格。比如改成下面这种格式:
let arr:[]=[111,'222',111];
- ts并没有报错,如果想要严格限制,则可以这样进行约束:
let arr:[number,string,number]=[111,'222',111];
Interface接口- 比如我们要做一个筛选,吧不符合条件的过滤出去,我们可能会这样写:
const types==>{age>=20 && height>=180 && console.log;age<=20 && height <180 &&console.log};types //符合条件
- 但如果又修改了一些需求,可能还会去大量变更代码,在开发中,代码能复用肯定是最好的,所以可以把一些重复的代码抽离出来:
interface People{ name:string; age:number; height:number;}const types==>{ people.age>=20 && people.height>=180 && console.log; people.age<=20 && people.height <180 &&console.log};const choose==>{ console.log}const people={ name:'tom', age:18, height:178}types;choose;
接口与类型别名的区别- 看起来两者没有什么区别,但有个小细节:类型别名可以直接给类型,接口必须代表对象
type People=string;interface People{ name:string; age:number; height:number;}
- 如果传入的参数中有不确定项,我们可以使用 : 来进行处理:
interface People{ name:string; age:number; height:number; say :string};const types==>{ people.age>=20 && people.height>=180 && console.log; people.age<=20 && people.height <180 &&console.log};const people={ name:'tom', age:18, height:178}const people2={ name:'tom', age:18, height:178, say:'hello'}types;types;
- 这时候又有新需求了,如何在后面加入任意多的字段?这时候我们就可以这么写:
interface People{ name:string; age:number; height:number; say :string; [propname:string]:any;}const types==>{ people.age>=20 && people.height>=180 && console.log; people.age<=20 && people.height <180 &&console.log};const people={ name:'tom', age:18, height:178, say:'hello', add:'new add', addNumber:123}types;
接口里的方法- 接口不仅仅可以存属性,也可以存方法:
interface People{ name:string; age:number; height:number; goto:string;}const types==>{ people.age>=20 && people.height>=180 && console.log; people.age<=20 && people.height <180 &&console.log};const people={ name:'tom', age:18, height:178, goto{ return 'hello' }}types
接口和类的约束- 在ES6中是有类的概念,类可以和接口相结合:
interface People{ name:string; age:number; height:number; goto:string;}const types==>{ people.age>=20 && people.height>=180 && console.log; people.age<=20 && people.height <180 &&console.log};const people={ name:'tom', age:18, height:178, say:'hello', add:'new add', addNumber:123, goto{ return 'hello' }};class newPeople implements People{ name='bob'; age=19; height:190; say:'hello'; add:'new add2'; addNumber:123; goto{ return 'hi' }};let a=new newPeople;console.log)types
接口的继承- 接口与接口也是可以继承的:
interface People{ name:string; age:number; height:number; say :string; [propname:string]:any; goto:string;}const types==>{ people.age>=20 && people.height>=180 && console.log; people.age<=20 && people.height <180 &&console.log};const people={ name:'tom', age:18, height:178, say:'hello', add:'new add', addNumber:123, goto{ return 'hello' }, back{ console.log }}interface newPeople extends People{ back:void}types
类的基本使用- 首先,我们先创建一个类:
class Test{ name='say' say{ return this.name }};
- 这就是平时所写的类,在ts中的继承和ES6的继承是一样的,关键字也是extends,比如我们这里新建个类,继承Text;
class Test{ name='say' say{ return this.name }};class NewTest extends Test{ back{ return 'hello' }}const tom=new NewTest;console.log);console.log);
super关键字的使用- 如果想在say方法中后面加点东西,可以这么操作:
class Test{ name='say' say{ return this.name }};class NewTest extends Test{ back{ return 'hello' } say{ return super.say+'----hello' }}const tom=new NewTest;console.log);console.log);
ts中类的访问类型- ts中的访问类型就是基于三个关键字:private、protected以及pubilc这三种访问类型。看例子,先定义一个类,然后用这个类的对象,进行赋值:
class Person{ name:string}const person=new Person;person.name='tom';console.log
pubilc- 运行可以看到正常的输出内容,这是因为如果不对name的访问属性进行定义,那么他的默认属性就是pubilc,从字面意思来看,它的意思就是公用的,允许在类的内部和外部被调用。
class Person{public name:string}const person=new Person;person.name='tom';console.log
private- private的属性意思就是只允许在类的内部调用,不能在外部调用
class Person{ private name:string; public say{ console.log; }}const person=new Person;person.name='tom';//报错console.log//报错
protected- protected允许在类内以及继承的子类中使用,把刚刚的name改成protected属性,这时候在外部就会报错,这时候再写一个继承,代码如下:
class Person{ protected name:string; public say{ console.log; }}const person=new Person;person.name='tom';//报错console.log//报错class NewPerson extends Person{ public back{ console.log }}const newperson=new NewPerson;newperson.back//正常
类的构造函数- 首先新建一个类:Person,定义一个name,并在new的时候进行参数传递,然后打印出来,这时候我们就可以使用构造函数constructor :
class Person{ pubilc name:string; constructor{ this.name=name }};const person=new Person;console.log;
- 可以看到可以打印出来,但是上面写法有点麻烦,还可以再进行简化:
class Person{ constructor{}};const person=new Person;console.log;
类继承中的构造器写法- 普通的书写方法上面已经演示了,在子类中使用构造函数需要用super调用父类的构造函数,直接看代码:
class Person{ constructor{}};class Teacher extends Person{ constructor{ super }};const teacher=new Teacher;console.log;
ts中类的Getter、Setter、static以及readonly- 在上面中提到了访问类型private,它的最大用处就是封装一个书写,然后通过Getter和Setter去访问和修改:
class Person{ constructor{ }}
- 如果想让别人知道,就可以使用Getter来实现,他并不是一个方法,只是一个属性:
class Person{ constructor{ }; get age{ return this._age }};const person=new Person;console.log
- 这时候你可能会觉得这不是多此一举吗?但在Getter中可以对 _age进行处理:
class Person{ constructor{ }; get age{ return this._age-10 }};const person=new Person;console.log
- 既然 _age是私有的,我们无法进行改变,这时候就可以用Setter进行改变:
class Person{ constructor{ }; get age{ return this._age } set age{ this._age=age }};const person=new Person;person.age=20;console.log
- 在类中,如果想用这个类的实例,就必须先进行new操作,但有没有一种方法不需要new就可以?
//常规方法 class Person{ say{ return 'say hello' }};const person=new Person;console.log);
- 在ts中,我们不想new的话可以这么写:
class Person{static say{ return 'say hello' }};console.log)
- readonly在初始化后赋值,以后就不能进行修改
class Person{ constructor{}}let p=new Person;p.name='bob'//报错console.log;
类的抽象类- abstract 用于定义抽象类和其中的抽象方法。它有几个特点:
- 抽象类是不允许实例化的
//错误演示abstract class Person { public abstract say}const tom=new Person//无法创建抽象类的实例
- 抽象类中的抽象方法必须被子类实现:
//错误演示abstract class Person { abstract say{ console.log }}class Tom extends Person{ say{ console.log }};//正确方法abstract class Person { abstract say}class Tom extends Person{ say{ console.log }};
tsconfig.json配置生成- 我们可以通过tsc --init去生成ts配置文件:
tsc --init //终端执行
编译- 会生成一个tsconfig.json文件,我们在ts文件中可以随便写点什么:
//测试tsconfig.jsonconst test:string='tsconfig.json';
- 然后打开tsconfig.json文件,找到complilerOptions属性下的removeComments:true(这个配置是编译后不输出注释),取消掉注释,然后执行命令:
tsc
- 这时候打开生成的js文件,发现没有注释,说明成功。
- 如果有多个ts文件,只想编译一个可以在ts配置项中加入include:
"include":["text.ts"],
- 如果想除了某个文件不编译,剩下的都编译,可以使用exclude:
"exclude":["text.ts"],
- files和include没有什么区别:
"files":["text.ts"],
compilerOptions配置removeComments- 这个配置意思就是编译后不输出注释
- 这个设置为true,就代表严格执行ts语法,要严格按照ts语法来编写。
- 允许你的注解类型 any 不用特意表明,如果此时设置了true,看例子:
//这时候编译会报错function test { return name;}//正确 function test { return name;}
strictNullChecks- 意思就是,不强制检查null类型,此时如果配置为true看例子:
//此时就不会报错const test: string = null;
outDir和rootDir- 此项配置是来指定文件目录和打包后存放目录,rootDir为文件目录,outDir为打包后保存的目录
{ "outDir": "./build" , "rootDir": "./src" ,}
编译ES6语法- 可以使用target和allowJs,target默认为true
"target":'es5' , // 这一项默认是开启的,你必须要保证它的开启,才能转换成功"allowJs":true, // 这个配置项的意思是联通
sourceMap- sourceMap 简单说,Source map 就是一个信息文件,里面储存着位置信息。
也就是说,转换后的代码的每一个位置,所对应的转换前的位置。有了它,出错的时候,除错工具将直接显示原始代码,而不是转换后的代码。这无疑给开发者带来了很大方便。
- 设置noUnusedLocals为true,编译代码:
const name:tring='111';export const age = "text";
- 这时候就会报错,因为有name变量没有使用。
- 更多可以查看 配置查询网站
- 联合类型的意思就是允许一个类型有两种或者两种以上的类型:
interface Test{ name:string; say:=>{ }}interface Test2{ name:string; call:=>{};}function Tom{ console.log}
- 如果此时修改一下方法:
function Tom{ fun.say}//报错
- 这是因为只能访问两个类型的共有方法。
- 上面的方法,如果修改完报错,这时候我们可以用as来判断:
interface Test { text: boolean; say: void}interface Test2 { text: boolean; skill:string}function judgeWho { if { .skill; }else{ .say; }}const a={ text:true, skill:function:void{ console.log }, say:function:void{ console.log }}judgeWho //1
类型保护-in语法- in方法与断言比较类似,使用方法如下:
interface Person{ name:string; say:=>{ }}interface Person2{ name:string; todo:=>{ }};function tom{ if{ val.todo }else{ val.say }}
类型保护-typeof语法- 可以用typeof方法来判断:
function add{ if{ return name+'---'+name2 }else{ return name+name2 }}add
类型保护-instanecof语法- 如果要保护类型是一个对象,就可以使用instanceof:
class NumObject{ num:number}function numbers{ if{ return num1.num+num2.num }}
Enum枚举类型- 我们平时会有这种写法:
function getName{ if{ return 'one' }else if{ return 'two' }else{ return 'three' }}console.log)
- 这么写可能有点麻烦,阅读起来还是有点麻烦,这时候可以这么写:
const Status={ ONE:0, TWO:1, THREE:3}function getName{ if{ return 'one' }else if{ return 'two' }else{ return 'three' }}console.log);
- 这时候我们的枚举就要上场了:
enum Status{ ONE, TWO, THREE}function getName{ if{ return 'one' }else if{ return 'two' }else{ return 'three' }}console.log);
- 一样也可以输出,因为枚举是有对应数字值的,默认从0开始,当然也可以改变:
enum Status{ ONE=1, TWO,//2 THREE//3}
- 也可以进行返查操作:
enum Status{ ONE, TWO, THREE}function getName{ if{ return 'one' }else if{ return 'two' }else{ return 'three' }}console.log;
泛型- 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
- 我们先编写一个函数:
function add{ return `${one}${two}`}console.log)
- 这时候我们希望参数更加灵活一些,两个参数为number或者string:
function add{ return `${one}${two}`}console.log)
- 这么书写有些麻烦,这时候就可以使用泛型:
function add{ return `${one}${two}`}console.log)
- 在使用中,泛型通常用
来进行表示。泛型可以有多个吗?当然可以:
function add{ return `${one}${two}`}console.log)
- 同时泛型也支持类型推断:
function add{ return `${one}${two}`}console.log)
在类中使用- 首先先写一个类,且接受参数为一个数组,数组里面存放string类型的数据:
class List { constructor {}; getItem:string{ return this.list[index] }};const list=new List;console.log)
- 这时候如果我们传递数组时候里面又想放数字怎么办:
class List { constructor {}; getItem:string|number{ return this.list[index] }};const list=new List;console.log)
- 这么写就比较复杂了,这时候就可以使用泛型来简化我们的代码:
class List{ constructor {}; getItem:T{ return this.list[index] }};const list=new List;console.log)
- 发现上面的代码没有报错?因为类型推论,所以不会报错,严格意义上应该new的时候加上类型:
const list=new List;
- 还有一种场景,传递过来是一个数组对象,这时候可以通过继承来解决:
interface People{ name:string}class List{ constructor {}; getItem:string{ return this.list[index].name }};const list=new List;console.log)
泛型约束- 上面例子中,泛型可以为任意值,但有时候我们希望还是能稍微约束一下:
function list{ return `${name}`}console.log)class List { constructor { }; getItem:T{ return this.list[index] }}const list=new Listconsole.log)
Namespace命名空间新建一个ts项目- 首先,我们建立一个项目文件,然后npm init -y生成package.json文件,然后再tsc -init生成ts配置文件。
- 在根目录下新建index.html文件,再建立一个src和bulid目录,在src目录下新建一个index.js。
- 配置tsconfig.json文件,设置入口和输出目录(outDir和rootDir)。
- 打开index.html,引入js文件:
- 在新建的ts文件中随便写点什么:
console.log;
- 然后tsc编译一下,打开控制台,我们就可以看到了
- 在我们刚刚建立的index.ts文件,写一个header、content和footer组件:
class Header { constructor { const elem = document.createElement; elem.innerText = "This is Header"; document.body.appendChild; }}class Content { constructor { const elem = document.createElement; elem.innerText = "This is Content"; document.body.appendChild; }}class Footer { constructor { const elem = document.createElement; elem.innerText = "This is Footer"; document.body.appendChild; }}class Page { constructor { new Header; new Content; new Footer; }}
- 然后在index.html中加一行js代码:
- 这时候我们可以看到内容正常输出,但有一个问题,我们的Header、content和footer都暴露了出来,并不是只暴露一个page,这时候我们的命名空间就派上了用场:
- 命名空间声明的关键词是namespace 比如声明一个namespace Home,需要暴露出去的类,可以使用export关键词,这样只有暴漏出去的类是全局的,其他的不会再生成全局污染了。