ts作为一个超级js,这两年是越来越火,我辈中人如何能不馋他,故观其大概后,迫不及待分享给大家,自己加深印象同时,又帮助大家,岂不美哉!本文分为三个部分,基础语法,进阶语法,以及高级语法,主要是讲解常用语法使用,方便以后忘记之处及时查阅!
由于ts和vscode都是巨硬出品,所以,在语法提示,以及语法检查上面相当友好,于是,编辑器选择首选vscode (巨硬大法真香万岁)
如上图,就是这个编辑器
既然搭建环境,nodejs一定得装,然后就是基于node 的一些包,比如 ts-node(在node 上运行,ts)typescript(给ts文件转化成浏览器能执行的js)
我们的package.json是这个样子基本就成了
由于TypeScript 不是「强类型」,是「静态类型检查」的「弱类型」所谓静态类型,我的理解就是在定义变量的时候给他限定一个类型比如:
var a:number=3
以上代码中我们就给ts定义了一个number的类型,如果你给他赋值为其他类型,那么编辑器就会报错
在严格的ts静态类型检查下,他就会报错,那么ts能声明哪些数据类型呢?
ts的值类型跟js基本差不多都有string、number、null、undefined、symbol、boolean,void 他们应该怎么声明静态类型呢?
//string
var a:string='a'
// number
var b:number=1
//boolean
var c:boolean=false
//由于null和undefined是这两个的的类型检查没啥意义所以我们可以用void来代替
var d:void=undefined
//symbol
//symbol用法跟js一样
var e=symbol()
引用类型会比js多一点,有function 、object、class、emun、array、Tuple,function等等那我么分别应该怎么写呢?
//object,你会发现声明对象的时候他的值也都被限定了,这就是静态类型的魅力
const teacher:{
a:number,
b:string
}={
a:1,
b:"好好学习"
}
//class
calss teacher{
a:string
}
//array
const teacher:number[]=[1,2,3,4,5]
//Tuple,当数组中由于种种限制,达不到的时候,元祖出现了,元祖不但要求类型一样,数量同样也要一样
const teacher:['string',number]=['好好学习',11]
//function,这个意思就是他是一个函数类型,而且返回值必须是函数
const tea:()=>number=()=>{
retrun 2
}
//emun枚举类型可以自动排序,从0开始,并且能反向查找
emen sea{
a,
b,
c
}
当然,你以为这些类型就完了么,那么你就错了,ts真正学起来有难度的地方,是这些类型注解混合使用而带来的一些坑!我们来看下面几个例子
1、如果一个函数中结构赋值,我们应该怎么办?
function a({b,c}:{a:number,b:string}):number{
retrun a+b
}
2、一个函数不需要返回值怎么办?
function():viod{
console.log('好好学习')
}
3、在类中的类型注解,想要拿到上一个类的实例应该怎么写?
class a{
ac:string='好好学习'
bc:string="天天向上"
}
class b{
//我们可以将属性的静态类型直接写为上个构造函数,就表示这个newa必须是一个类a对应的对象
c:newa:a
constructor(){
this.newa=new a()
}
}
复制代码
所谓Type inferenct 类型注解,就是,在我们声明变量的时候告诉ts是什么类型
所谓Type Inference 类型推断:当没有显式指定类型注解时,编译器会推断出一个类型。
我们发现当我们声明赋值以后,编辑器会自动推断出一个类型,在以后再赋值时,act的类型就被锁死
在ts中类的定义和继承是和es6基本一致,只不过在此基础上加上了一些类型注解
1、类的定义
class student{
name:string
constructor(){
//new的时候回去执行这个constructor
this.name='haohaoxuexi'
}
eat(){
console.log(this.name)
}
}
复制代码
2、类的继承
继承和js的继承类似一个extends即可
class Teacher extends student {
public sayBye() {
//此处就能拿到父类的方法
this.eat();
}
}
复制代码
注:super在子类的constructor中调用,是在子类中执行了父类的构造函数,。如果你不在constructor里写super并传入相应参数,那么相当于只继承原型方法。 每个类自己的constructor其实就是定义自身的属性和方法,而不是原型上的。可以直接使用this.abc来添加,this指自己,super指父类。 子类继承的时候不写constructor,则默认会把父类自身的属性和方法生成到子类。
3、类的重写
所谓重写就是子类重写父类方法
class Teacher extends student {
public sayBye() {
//此处就能拿到父类的方法
}
//重写
eat(){
//此处有个小坑,我们要向重写之后在使用父类的方法咋办?使用super
super.eat()//如此就能拿到父类的方法了
};
}
复制代码
** 4、类的访问类型**
类中的属性和方法有四种访问类型
5、类的getter和setter
由于在js中,getter 和setter不能直接使用,我们需要通过一个Object.defineProperty来定义触发,那么在ts中就简单多了在类中直接能声明
class teacher {
private _a: string = '12'
get eat() {
console.log(this._a)
return this._a
}
set eat(name: string) {
console.log(this._a)
this._a = name
}
}
let teacher1 = new teacher()
console.log(teacher1.eat)
teacher1.eat="12345"
复制代码
6、static的妙用
staic这个关键字是给这个方法直接挂在类上,而不是new出来的实例,那他有啥用处呢?最直接了当的就是设计模式中经典的单例模式,用它最合适不过了!
class danli{
//先声明一个存单例要用的变量
private static danli:danli
static getDanli(){
//判断是否有单例了
if(!this.danli){
this.danli=new danli()
}
//返回
return this.danli;
}
test(){
console.log(this)
}
}
const a1=danli.getDanli()
const a2=danli.getDanli()
复制代码
7、抽象类
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。 abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。具体
//这就是一个抽象类,不能被实例化
abstract class ment {
printName(): void {
console.log(this)
}
//方法如果也有这个字段,那么就是个抽象方法,如果有类继承,那么需要实现这个方法
abstract meeting(): void
}
class fangfa extends ment{
meeting(){
console.log(this)
}
}
复制代码
所谓接口在ts一般指这个变量拥有什么东西,他有什么用呢?
//直接用关键字声明即可
interface teacher{
name:string,
//表示可有可无
sx?:string,
// 表示是可以有其他类型只能是 Key 为 string 类型,而 Value 为 number 类型。
[propName: string]: number,
//还能有个方法
say():string
}
那我们应该怎么用呢?直接在在需要声明静态类型的地方直接使用即可
var dom:tacher={
name:'haohaoxuex'
}
一个类还能应用一个接口
class b implements teacher{
//里面需要继承所有接口的属性和方法
}
一个接口还能实现继承,同样的跟类的继承一样extends关键字加上即可
1、联合类型
所谓联合类型是用于限制传入的值的类型只能是 | 分隔的每个类型,比如:
interface a1 {
name: string;
age: number;
}
interface a2 {
love: string;
age: number;
}
let met:a1|a2
如果一个值是联合类型,那么我们只能访问它们中共有的部分(共有的属性与方法),由于只能访问共有,导致我们在想要访问某一个的时候ts会提示报错,这时我们就需要类型保护了
2、类型保护
怎么做类型保护呢,我们有那么几种方式
met={
name: 'haohaoxu',
age: 18,
love: 'haohao'
}
//使用as直接断言,告诉ts在哪里去找
if((me as a1).name) {
console.log((me as a1).name);
}
if((me as a1).name) {
console.log((me as a2).love);
}
//使用in
if(('name' in me)) {
console.log(me.name);
}
if('love' in me) {
console.log(me.love);
}
//使用typeof
function add(one:number|string,two:number|string){
if(typeof one=="string"||typeof two=="string"){
retrun `${one}${two}`
}
retrun one+two
}
//使用instanceof
class a{
num:1
}
function b(obj:object|a){
if(obj instanceof a){
retrun obj.num
}
}
什么是泛型呢,我的理解就是泛指的类型,那他在ts中应该怎么写呢?
//定义是用尖括号表示一个变量
function add<T>(one:T,two:T){
}
//调用的时候去声明类型
add<string>("12","34")
上面就是泛型,他的用途就是如果当你不确定要传的值的时候,那么就使用泛型在传入的时候去确定 是不是很简单啊?那么你又错了,下面我们来看几种情况:
1、如果想传一个数组怎么办?
function add<T>(one:T[]){
}
add<string>(["1"])
2、如果我要传两个泛型怎么办?
function add<T,D>(one:T,two:D){
}
add<string,string>("1","2")
3、在类中如何使用泛型呢?
class add<T>{
constructor(one:T){
}
}
var a=new add<string>("1")
4、你知道泛型还能继承吗?
class add<T extends number|string>{
constructor(one:T){
}
}
var a=new add<string>("1")
5、泛型怎么去做类型注解呢?
//这块有点绕,大致意思就是,给泛型用在类型注解中
var add:<T>(a:T)=>{}=<T>(a:T)=>{}
6、泛型中使用keyof(比较难) keyof是啥,我认为他就是for循环,那他应该咋么用呢?
interface abc{
name:string,
age:number,
get:boolean
}
var a={
name:'dddd',
age:11,
get:false
}
function add<T extends keyof abc>(key:T):abc[T]{
return a[key]
}
//如此一来我们便能知道返回值的准确类型了
var a1=add('name')
如果我赋值少一个便会报错!
ts在我们使用的时候如果用面向对象的方式声明多个类生成实例的时候,你会发现在全局就会多出几个实例,这样就会导致全局污染,如此一来,我们便需要namespace这个关键字,来防止全局污染
namespace Main{
class a{}
class b{}
//如果想要导出给外部使用,需要导出
export class c{}
}
//这样在全局只会有一个home供我们使用了
如果,由于我们的ts语言有着大量的类型注解,但是在实际的开发中,我们的ts文件需要引入一些,js的库供我们使用,此时,静态类型检查就会报错,以jq来举例,我们如果引入jq后,当使用$这个jq的经典符号的时候,编辑器就会飘红
那我们应该咋么办呢?很简单
//定义一个全局类型
declare var $:()=>void
$()
此时我们就会发现不报错了
而且编译之后declare定义的这个全局变量也消失了,那有的大佬会问了,那我么引入js库的时候是不是很麻烦,其实行业大佬已经给我们想好了,塔恩他们会提供相应的表述文件的npm包比如 @types/jquery就是jq的描述文件
有时候我们在编写类库的时候,我们一般会用 typescript-library-starter这个脚手架,他会给我们快速初始化一个ts环境的脚手架,方便我们快速开发,但是如果我们要但是如果我们的轮子需要在script中直接引用,那么我们就需要在全局去挂一个方法,供别人调用,来看代码
interface Window {
teacher: any;
}
class teacher{
private a:string='12'
eat(){
console.log(this.a)
}
}
window.teacher=teacher
复制代码
这样就能在window下挂载一个方法了,这其实是个笨办法,一般情况下我们只需要使用umd规范的包便可直接引入,挂出这个方法的目的其实是,请教一些大佬为啥这样写ts能识别,我在文档中并未找到
ts中,装饰器是相当强大的存在,不过如今es7也支持了装饰器,那么一石二鸟,一块学了吧,他们其实差不多
那么什么是装饰器呢?他有哪些特性呢?
废话少说,上代码:
//定义一个装饰器
funciton lol(constructor:any){
//接受一个构造函数,可以任意天机方法
constructor.prototype.name=function(){
console.log(0)
}
}
//用@符号使用即可
@lol
class uzi{
}
复制代码
是不是好像很简单,接下来一点难的,不要眨眼睛认真看!
1、装饰器配合泛型的高端写法
//new的意思表示这个这是一个构造函数,返回值是一个any类型,接受很多any类型的参数的数组,然后T继承了这个构造函数
function lol<T extends new (...args: any[]) => any>(constructor: T){
//返回一个类继承constructor,我的理解就是装饰器给重写了
return class extends constructor{
name="123"
}
}
//用@符号使用即可
@lol
class uzi{
constructor(name:string){
this.name="1"
}
2、装饰器也能使用工厂模式(相当难)
由于在装饰器中添加类的方法,ts识别不了,所以我们必须用as来给他类型断言,但是这种写法相当的不优雅,我们可以用装饰器封装一个工厂模式来解决
//工厂封装retrun一个真正的装饰器
function lol(){
//new的意思表示这个这是一个构造函数,返回值是一个any类型,接受很多any类型的参数的数组,然后T继承了这个构造函数
return function <T extends new (...args: any[]) => any>(constructor: T){
//返回一个类继承constructor,我的理解就是装饰器给重写了
return class extends constructor{
// name="123"
get(){
}
}
}
}
//返回的装饰器去修饰这个匿名类,在赋值给uzi就能拿到了
const uzi = lol()(
class{
name:string
constructor(){
this.name="1"
}
}
)
const test=new uzi()
复制代码
你会发现,真的不报错了
3、方法也能使用装饰器
function lol(target: any, key: string, descriptor: PropertyDescriptor) {
// 普通方法,target 对应的是类的 prototype
// static定义的静态方法,target 对应的是类的构造函数
//key表示方法的名字
//descriptor跟Object.defineProperty()中的用法类似,例如writable表示外部能不能改,value他的值
descriptor.value = function() {
return 'theshy';
};
}
class uzi {
name: string;
constructor(name: string) {
this.name = name;
}
@lol
getName() {
return this.name;
}
}
const test = new uzi('adc');
复制代码
其实,属性,访问器甚至参数都能使用装饰器,用法也很简单,不在赘述,请大家自行查阅文档
4、装饰器妙用,实现复用
当类中的每个方法都需要处理一个逻辑的时候,那么我们的装饰器的用处来了,上代码:
//工厂函数
function lol(name:string){
return function(target: any, key: string, descriptor: PropertyDescriptor) {
//取到之前方法
const fn=descriptor.value
//重写之前方法
descriptor.value=function(){
try{
fn()
}catch(e){
console.log(`${name}找不到`)
}
}
}
}
class uzi {
name: any=null;
// constructor(name: string) {
// this.name = name;
// }
@lol('theshy')
getName() {
return this.name.theshy;
}
@lol('uzi')
getName1() {
return this.name.uzi;
}
}
const test = new uzi();
复制代码
ok,ts的基础基本基本完了,由于是笔记类型的文章,主旨是告诉大家知识结构图谱,深度用法不多剖析,大家可以根据要点自行查询文档,一旦知识体系形成,还愁不会用ts吗?之说笔记没有写的很细,是由于我觉得想要学会一个知识,得几个前提条件,首先是好奇心,其次是上进心,在然后是耻辱,最后是被逼无奈,所以,你看那些非常细致的文章,往往你看一遍就忘了,因为在看文章的时候没有具备以上四点中的一点或者多点,所以,有心人只要有入门的知识脉络,他顺藤摸瓜,加入自己的思考,和实践,很快就能掌握,而如果只是看看热闹,那么对不起,写的再细致也学不会。所以,并没有把知识嚼烂了喂给大家,还请大家自行研究思考。
我学习ts基础的笔记基本无私的奉献给大家了,如有大佬看到不对之处,还请批评指正!(由于示例代码太多,难免有错误,如果跑不起来或者错误的地方,请大佬评论指出,我及时改正)
本文是慕课大佬Dell lee的的实战总结笔记,虽然花了钱,银货两讫,但是还是要在此感谢大佬认真准备的课程对我们的帮助,以及对前端行业的贡献