好文: ts是啥意思啊

懵懂先生 投稿文章好文: ts是啥意思啊已关闭评论86阅读模式

文章源自略懂百科-http://wswcn.cn/108198.html

TS 是什么 ?

TS:是TypeScript的简称,是一种由微软开发的自由和开源的编程语言。文章源自略懂百科-http://wswcn.cn/108198.html

TS和JS的关系

对比与JS,TS是JS的超集,简单的说就是在JavaScript的基础上加入了类型系统,让每个参数都有明确的意义,从而带来了更加智能的提示。文章源自略懂百科-http://wswcn.cn/108198.html

相对于JS而言,TS属于强类型语言,所以对于项目而言,会使代码更加规范,从而解决了大型项目代码的复杂性,其次,浏览器是不识别TS的,所以在编译的时候,TS文件会先编译为JS文件。文章源自略懂百科-http://wswcn.cn/108198.html

安装TS

执行命令:文章源自略懂百科-http://wswcn.cn/108198.html

$ npm install -g typescript//或$ yarnglobaladdtypescript
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

查看版本

$ tsc -v
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

编译

$ tsc test.tstest.ts => test.js复制代码文章源自略懂百科-http://wswcn.cn/108198.html

在线编译

我们为了方便起见,可以使用线上的编辑器:TypeScript Playground[2],像这样文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

并且你还可以看看生成对应的ts转化ES5,ES6之后的代码,也有相关的例子供你查看文章源自略懂百科-http://wswcn.cn/108198.html

TS的基本数据类型

这里将TS的数据类型简单的进行下归类:文章源自略懂百科-http://wswcn.cn/108198.html

基本类型:string、number、boolean、symbol、bigint、null、undefined引用类型:array、Tuple(元组)、object(包含Object和{})、function特殊类型:any、unknow、void、nerver、Enum(枚举)其他类型:类型推理、字面量类型、交叉类型文章源自略懂百科-http://wswcn.cn/108198.html

注:案例中有可能用到type和interface,在下面会详细讲解,有比较模糊的可以先看看文章源自略懂百科-http://wswcn.cn/108198.html

基本类型

//字符串letstr:string="Domesy"// 数字letnum:number=7//布尔letbool:boolean=true//symbolletsym: symbol = Symbol();//bigintletbig: bigint =10n//nullletnu:null=null//undefinedletun:undefined=undefined复制代码文章源自略懂百科-http://wswcn.cn/108198.html

需要注意:文章源自略懂百科-http://wswcn.cn/108198.html

null和undefined两个类型一旦赋值上,就不能在赋值给任何其他类型symbol是独一无二的,假设在定义一个sym1,那么sym === sym1 为 false文章源自略懂百科-http://wswcn.cn/108198.html

引用类型

Array

两种方式:文章源自略懂百科-http://wswcn.cn/108198.html

类型名称 + []Array<数据类型>letarr1:number[] = [1,2,3]letarr2:Array<number> = [1,2,3]letarr2:Array<number> = [1,2,3]// error//要想是数字类型或字符串类型,需要使用 |letarr3:Array<number|string> = [1,2,3]//ok复制代码文章源自略懂百科-http://wswcn.cn/108198.html

Tuple(元组)

Tuple可以说是Array的一种特殊情况,针对上面的arr3,我们看他的类型可以是string也可以是number,但对每个元素没有作出具体的限制。文章源自略懂百科-http://wswcn.cn/108198.html

那么Tuple的作用就是限制元素的类型并且限制个数的数组,同时Tuple这个概念值存在于TS,在JS上是不存在的文章源自略懂百科-http://wswcn.cn/108198.html

这里存在一个问题:在TS中,是允许对Tuple扩增的(也就是允许使用push方法),但在访问上不允许文章源自略懂百科-http://wswcn.cn/108198.html

lett: [number,string] = [1,2]// oklett1: [number,string] = [1,3]// errorlett2: [number,string] = [1]// errorlett3: [number,string] = [1,1,true]// errorlett5: [number,string] = [1,2]// okt.push(2)console.log(t)// [1, 2, 2]leta = t[0]// okletb = t[1]// okletc = t[2]// error复制代码文章源自略懂百科-http://wswcn.cn/108198.html

object

object非原始类型,在定义上直接使用 object 是可以的,但你要更改对象的属性就会报错,原因是并没有使对象的内部具体的属性做限制,所以需要使用{}来定义内部类型let obj1:object={a:1,b:2}obj1.a=3//errorlet obj2:{a:number,b:number}={a:1,b:2}obj2.a=3//ok复制代码Object(大写的O),代表所有的原始类型或非原始类型都可以进行赋值,除了null和`undefinedletobj:Object;
obj =1;// okobj ="a";// okobj =true;// okobj = {};// okobj =Symbol()//okobj =10n//okobj =null;// errorobj =undefined;// error复制代码文章源自略懂百科-http://wswcn.cn/108198.html

function

定义函数

有两种方式,一种为function, 另一种为箭头函数在书写的时候,也可以写入返回值的类型,如果写入,则必须要有对应类型的返回值,但通常情况下是省略,因为TS的类型推断功能够正确推断出返回值类型functionsetName1(name: string){ //ok
console.log("hello", name);
}
setName1("Domesy"); //"hello","Domesy"functionsetName2(name: string):string{ //errorconsole.log("hello", name);
}
setName2("Domesy");functionsetName3(name: string):string{ //errorconsole.log("hello", name);return1}
setName3("Domesy");functionsetName4(name: string):string{ //ok
console.log("hello", name);returnname
}
setName4("Domesy"); //"hello","Domesy"//箭头函数与上述同理
const setName5 = (name:string) => console.log("hello", name);
setName5("Domesy") //"hello","Domesy"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

参数类型

可选参数:如果函数要配置可有可无的参数时,可以通过?实现,切可选参数一定要在最后面默认参数:函数内可以自己设定其默认参数,用=实现剩余参数:仍可以使用扩展运算符...// 可选参数constsetInfo1 =(name:string, age?:number) =>console.log(name, age)
setInfo1(Domesy)//"Domesy", undefinedsetInfo1(Domesy,7)//"Domesy", 7// 默认参数constsetInfo2 =(name:string, age:number= 11) =>console.log(name, age)
setInfo2(Domesy)//"Domesy", 11setInfo2(Domesy,7)//"Domesy", 7// 剩余参数constallCount =(...numbers:number[]) =>console.log(`数字总和为:${numbers.reduce((val, item) => (val += item),0)}`)
allCount(1,2,3)//"数字总和为:6"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

函数重载

函数重载:是使用相同名称和不同参数数量或类型创建多个方法的一种能力。在 TypeScript 中,表现为给同一个函数提供多个函数类型定义。简单的说:可以在同一个函数下定义多种类型值,总后汇总到一块文章源自略懂百科-http://wswcn.cn/108198.html

letobj:any= {};functionsetInfo(val:string):void;functionsetInfo(val:number):void;functionsetInfo(val:boolean):void;functionsetInfo(val:string|number|boolean):void{if(typeofval ==="string") {
obj.name = val;
}else{
obj.age = val;
}
}
setInfo("Domesy");
setInfo(7);
setInfo(true);console.log(obj);// { name: Domesy, age: 7 }复制代码文章源自略懂百科-http://wswcn.cn/108198.html

特殊类型

any

在 TS 中,任何类型都可以归于any类型,所以any类型也就成了所有类型的顶级类型,同时,如果不指定变量的类型,则默认为any类型, 当然不推荐使用该类型,因为这样丧失了TS的作用文章源自略懂百科-http://wswcn.cn/108198.html

letd:any; //等价于 let dd=1;d=2;d=true;d=[1, 2, 3];d={}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

unknow

与any一样,都可以作为所有类型的顶级类型,但unknow更加严格,那么可以说除了any之下的第二大类型,接下来对比下any,主要严格于一下两点:文章源自略懂百科-http://wswcn.cn/108198.html

unknow会对值进行检测,而类型any不会做检测操作,说白了,any类型可以赋值给任何类型,但unknow只能赋值给unknow类型和any类型unknow不允许定义的值有任何操作(如 方法,new等),但any可以letu:unknown;leta:any;文章源自略懂百科-http://wswcn.cn/108198.html

u =1;//oku =2;//oku =true;//oku = [1,2,3];//oku = {};//okletvalue:any= u//okletvalue1:any= a//okletvalue2:unknown = u//okletvalue3:unknown = a//okletvalue4:string= u//errorletvalue5:string= a//okletvalue6:number= u//errorletvalue7:number= a//okletvalue8:boolean= u//errorletvalue9:boolean= a//oku.set()// errora.set()//oku()// errora()//oknewu()// errornewa()//ok复制代码文章源自略懂百科-http://wswcn.cn/108198.html

void

当一个函数,没有返回值时,TS会默认他的返回值为void类型文章源自略懂百科-http://wswcn.cn/108198.html

constsetInfo =():void=>{}// 等价于 const setInfo = () => {}constsetInfo1 = ():void=>{return1}// errorconstsetInfo2 = ():void=>{return2}// errorconstsetInfo3 = ():void=>{returntrue}// errorconstsetInfo4 = ():void=>{return}// okconstsetInfo5 = ():void=>{returnundefined}//ok复制代码文章源自略懂百科-http://wswcn.cn/108198.html

never

表示一个函数永远不存在返回值,TS会认为类型为never,那么与void相比,never应该是void子集, 因为void实际上的返回值为undefined,而never连undefined也不行文章源自略懂百科-http://wswcn.cn/108198.html

符合never的情况有:当抛出异常的情况和无限死循环文章源自略懂百科-http://wswcn.cn/108198.html

leterror =():never=>{// 等价约 let error = () => {}thrownewError("error");
};leterror1 = ():never=>{while(true){}
}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

Enum(枚举)

可以定义一些带名字的常量,这样可以清晰表达意图创建一组有区别的用例文章源自略懂百科-http://wswcn.cn/108198.html

注意:文章源自略懂百科-http://wswcn.cn/108198.html

枚举的类型只能是string或number定义的名称不能为关键字文章源自略懂百科-http://wswcn.cn/108198.html

同时我们可以看看翻译为ES5是何样子文章源自略懂百科-http://wswcn.cn/108198.html

数字枚举

枚组的类型默认为数字类型,默认从0开始以此累加,如果有设置默认值,则只会对下面的值产生影响同时支持反向映射(及从成员值到成员名的映射),但智能映射无默认值的情况,并且只能是默认值的前面文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

字符串枚举

字符串枚举要注意的是必须要有默认值,不支持反向映射文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

常量枚举

除了数字类型和字符串类型之外,还有一种特殊的类型,那就是常量枚组,也就是通过const去定义enum,但这种类型不会编译成任何JS,只会编译对应的值文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

异构枚举

包含了数字类型和字符串类型的混合,反向映射一样的道理文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

类型推论

我们在学完这些基础类型,我们是不是每个类型都要去写字段是什么类型呢?其实不是,在TS中如果不设置类型,并且不进行赋值时,将会推论为any类型,如果进行赋值就会默认为类型文章源自略懂百科-http://wswcn.cn/108198.html

leta;// 推断为anyletstr= 小杜杜;// 推断为stringletnum =13;// 推断为numberletflag =false;// 推断为booleanstr=true// error Type boolean is not assignable to type string.(2322)num =Domesy// errorflag =7// error复制代码文章源自略懂百科-http://wswcn.cn/108198.html

字面量类型

字面量类型:在TS中,我们可以指定参数的类型是什么,目前支持字符串、数字、布尔三种类型。比如说我定义了str 的类型是 小杜杜那么str的值只能是小杜杜文章源自略懂百科-http://wswcn.cn/108198.html

letstr:小杜杜letnum:1|2|3=1letflag:truestr= 小杜杜//okstr=Donmesy// errornum =2//oknum =7// errorflag =true// okflag =false// error复制代码文章源自略懂百科-http://wswcn.cn/108198.html

交叉类型(&)

交叉类型:将多个类型合并为一个类型,使用&符号连接,如:文章源自略懂百科-http://wswcn.cn/108198.html

typeAProps= { a: string }typeBProps= { b: number }typeallProps= AProps & BPropsconstInfo: allProps = {
a: 小杜杜,
b:7}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

同名基础属性合并

我们可以看到交叉类型是结合两个属性的属性值,那么我们现在有个问题,要是两个属性都有相同的属性值,那么此时总的类型会怎么样,先看看下面的案列:文章源自略懂百科-http://wswcn.cn/108198.html

typeAProps={a:string,c:number}typeBProps={b:number,c:string}typeallProps=AProps&BPropsconst Info:allProps={a:小杜杜,b:7,c:1,//error(property)c:neverc:Domesy,//error(property)c:never}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

如果是相同的类型,合并后的类型也是此类型,那如果是不同的类型会如何:文章源自略懂百科-http://wswcn.cn/108198.html

我们在Aprops和BProps中同时加入c属性,并且c属性的类型不同,一个是number类型,另一个是string类型文章源自略懂百科-http://wswcn.cn/108198.html

现在结合为allProps后呢? 是不是c属性是number或string类型都可以,还是其中的一种?文章源自略懂百科-http://wswcn.cn/108198.html

然而在实际中,c传入数字类型和字符串类型都不行,我么看到报错,现实的是c的类型是 never文章源自略懂百科-http://wswcn.cn/108198.html

这是因为对应c属性而言是string & number,然而这种属性明显是不存在的,所以c的属性是never文章源自略懂百科-http://wswcn.cn/108198.html

同名非基础属性合并

interfaceA { a:number}interfaceB { b:string}interfaceC {
x: A
}interfaceD {
x: B
}typeallProps = C & DconstInfo: allProps = {
x: {
a:7,
b:小杜杜}
}console.log(Info)// { x: { "a": 7, "b": "小杜杜" }}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

我们来看看案例,对于混入多个类型时,若存在相同的成员,且成员类型为非基本数据类型,那么是可以成功合。文章源自略懂百科-http://wswcn.cn/108198.html

如果 接口A 中的 也是 b,类型为number,就会跟同名基础属性合并一样文章源自略懂百科-http://wswcn.cn/108198.html

Class(类)

在ES6中推出了一个叫class(类)的玩意,具体定义就不说了,相信用过React的小伙伴一定不陌生.文章源自略懂百科-http://wswcn.cn/108198.html

基本方法

在基本方法中有:静态属性,静态方法、成员属性、成员方法、构造器、get set方法,接下来逐个看看:文章源自略懂百科-http://wswcn.cn/108198.html

需要注意的是:在成员属性中,如果不给默认值,并且不使用是会报错的,如果不想报错就给如 **!**,如:name4!:string文章源自略懂百科-http://wswcn.cn/108198.html

classInfo {//静态属性staticname1:string=Domesy//成员属性,实际上是通过public上进行修饰,只是省略了nmae2:string=Hello//okname3:string//errorname4!:string//ok 不设置默认值的时候必须加入 !//构造方法constructor(_name:string){this.name4 = _name
}//静态方法staticgetName =()=>{return我是静态方法}//成员方法getName4 =()=>{return`我是成员方法:${this.name4}`}//get 方法getname5(){returnthis.name4
}//set 方法setname5(name5){this.name4 = name5
}
}constsetName =newInfo(你好)console.log(Info.name1)// "Domesy"console.log(Info.getName())// "我是静态方法"console.log(setName.getName4())// "我是成员方法:你好"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

让我们看看上述代码翻译成ES5是什么样:文章源自略懂百科-http://wswcn.cn/108198.html

"use strict";varInfo =/** @class */(function(){//构造方法functionInfo(_name){var_this =this;//成员属性this.nmae2 =Hello;//ok//成员方法this.getName4 =function(){return"\u6211\u662F\u6210\u5458\u65B9\u6CD5:".concat(_this.name4);
};this.name4 = _name;
}Object.defineProperty(Info.prototype,"name5", {//get 方法get:function(){returnthis.name4;
},//set 方法set:function(name5){this.name4 = name5;
},enumerable:false,configurable:true});//静态属性Info.name1 =Domesy;//静态方法Info.getName =function(){return我是静态方法;
};returnInfo;
}());varsetName =newInfo(你好);console.log(Info.name1);// "Domesy"console.log(Info.getName());// "我是静态方法"console.log(setName.getName4());// "我是成员方法:你好"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

私有字段()

在 TS 3.8版本便开始支持ECMACMAScript的私有字段。文章源自略懂百科-http://wswcn.cn/108198.html

需要注意的是私有字段与常规字段不同,主要的区别是:文章源自略懂百科-http://wswcn.cn/108198.html

私有字段以字符开头,也叫私有名称;每个私有字段名称都唯一地限定于其包含的类;不能在私有字段上使用 TypeScript 可访问性修饰符(如 public 或 private);私有字段不能在包含的类之外访问,甚至不能被检测到。classInfo {
name:string;//私有字段getName:string;constructor(name:string) {this.name = name;this.getName = name
}文章源自略懂百科-http://wswcn.cn/108198.html

setName() {return`我的名字是${this.name}`}
}letmyName =newInfo("Domesy");console.log(myName.setName())// "我的名字是Domesy"console.log(myName.getName)// ok "Domesy"console.log(myName.name)// error// Property name is not accessible outside class Info// because it has a private identifier.(18013)复制代码文章源自略懂百科-http://wswcn.cn/108198.html

只读属性(readonly)

只读属性:用readonly修饰,只能在构造函数中初始化,并且在TS中,只允许将interface、type、class上的属性标识为readonly文章源自略懂百科-http://wswcn.cn/108198.html

readonly实际上只是在编译阶段进行代码检查被radonly修饰的词只能在constructor阶段修改,其他时刻不允许修改classInfo{publicreadonlyname:string;// 只读属性name1:stringconstructor(name:string){this.name = name;this.name1 = name;
}文章源自略懂百科-http://wswcn.cn/108198.html

setName(name:string) {this.name = name// errorthis.name1 = name;// ok}
}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

继承(extends)

继承:是个比较重要的点,指的是子可以继承父的思想,也就是说子类通过继承父类后,就拥有了父类的属性和方法,这点与HOC有点类似文章源自略懂百科-http://wswcn.cn/108198.html

这里又个super字段,给不知道的小伙伴说说,其作用是调用父类上的属性和方法文章源自略懂百科-http://wswcn.cn/108198.html

// 父类classPerson {
name:stringage:numberconstructor(name:string, age:number){this.name = namethis.age = age
}文章源自略懂百科-http://wswcn.cn/108198.html

getName(){console.log(`我的姓名是:${this.name}`)returnthis.name
}文章源自略懂百科-http://wswcn.cn/108198.html

setName(name:string){console.log(`设置姓名为:${name}`)this.name = name
}
}// 子类classChildextendsPerson {
tel:numberconstructor(name:string, age:number, tel:number){super(name, age)this.tel = tel
}文章源自略懂百科-http://wswcn.cn/108198.html

getTel(){console.log(`电话号码是${this.tel}`)returnthis.tel
}
}letres =newChild("Domesy",7,123456)console.log(res)// Child {."name": "Domesy", "age": 7, "no": 1 }console.log(res.age)// 7res.setName(小杜杜)// "设置姓名为:小杜杜"res.getName()// "我的姓名是:小杜杜"res.getTel()// "电话号码是123456"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

修饰符

主要有三种修饰符:文章源自略懂百科-http://wswcn.cn/108198.html

public:类中、子类内的任何地方、外部都能调用protected:类中、子类内的任何地方都能调用,但外部不能调用private:类中、子类内的任何地方、外部均不可调用classPerson {publicname:stringprotectedage:numberprivatetel:numberconstructor(name:string, age:number, tel:number){this.name = namethis.age = agethis.tel = tel
}
}classChildextendsPerson {constructor(name:string, age:number, tel:number) {super(name, age, tel);
}文章源自略懂百科-http://wswcn.cn/108198.html

getName(){console.log(`我的名字叫${this.name},年龄是${this.age}`)// ok name 和 age可以console.log(`电话是${this.tel}`)// error 报错 原因是 tel 拿不出来}
}constres =newChild(Domesy,7,123456)console.log(res.name)// ok Domesyconsole.log(res.age)// errorconsole.log(res.tel)// error复制代码文章源自略懂百科-http://wswcn.cn/108198.html

abstract

abstract: 用abstract关键字声明的类叫做抽象类,声明的方法叫做抽象方法文章源自略懂百科-http://wswcn.cn/108198.html

抽象类:指不能被实例化,因为它里面包含一个或多个抽象方法。抽象方法:是指不包含具体实现的方法;文章源自略懂百科-http://wswcn.cn/108198.html

注:抽象类是不能直接实例化,只能实例化实现了所有抽象方法的子类文章源自略懂百科-http://wswcn.cn/108198.html

abstractclassPerson {constructor(publicname:string){}// 抽象方法abstractsetAge(age:number) :void;
}classChildextendsPerson {constructor(name:string) {super(name);
}文章源自略懂百科-http://wswcn.cn/108198.html

setAge(age:number):void{console.log(`我的名字是${this.name},年龄是${age}`);
}
}letres =newPerson("小杜杜")//errorletres1 =newChild("小杜杜");文章源自略懂百科-http://wswcn.cn/108198.html

res1.setAge(7)// "我的名字是小杜杜,年龄是7"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

重写和重载

重写:子类重写继承自父类中的方法重载:指为同一个函数提供多个类型定义,与上述函数的重载类似// 重写classPerson{
setName(name:string){return`我的名字叫${name}`}
}classChildextendsPerson{
setName(name:string){return`你的名字叫${name}`}
}constyourName =newChild()console.log(yourName.setName(小杜杜))// "你的名字叫小杜杜"// 重载classPerson1{
setNameAge(name:string):void;
setNameAge(name:number):void;
setNameAge(name:string|number){if(typeofname ===string){console.log(`我的名字是${name}`)
}else{console.log(`我的年龄是${name}`)
}
};
}constres =newPerson1()
res.setNameAge(小杜杜)// "我的名字是小杜杜"res.setNameAge(7)// "我的年龄是7"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

TS断言和类型守卫

TS断言

分为三种:类型断言、非空断言、确定赋值断言文章源自略懂百科-http://wswcn.cn/108198.html

当断言失效后,可能使用到:双重断言文章源自略懂百科-http://wswcn.cn/108198.html

类型断言

在特定的环境中,我们会比TS知道这个值具体是什么类型,不需要TS去判断,简单的理解就是,类型断言会告诉编译器,你不用给我进行检查,相信我,他就是这个类型文章源自略懂百科-http://wswcn.cn/108198.html

共有两种方式:文章源自略懂百科-http://wswcn.cn/108198.html

尖括号as:推荐//尖括号letnum:any=小杜杜letres1:number= (<string>num).length;// React中会 error// as 语法letstr:any=Domesy;letres:number= (strasstring).length;
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

但需要注意的是:尖括号语法在React中会报错,原因是与JSX语法会产生冲突,所以只能使用as语法文章源自略懂百科-http://wswcn.cn/108198.html

非空断言

在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符!可以用于断言操作对象是非 null 和非 undefined 类型。文章源自略懂百科-http://wswcn.cn/108198.html

我们对比下ES5的代码文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

我们可以看出来!可以帮助我们过滤null和undefined类型,也就是说,编译器会默认我们只会传来string类型的数据,所以可以赋值为str1文章源自略懂百科-http://wswcn.cn/108198.html

但变成ES5后!会被移除,所以当传入null的时候,还是会打出null文章源自略懂百科-http://wswcn.cn/108198.html

确定赋值断言

在TS2.7版本中引入了确定赋值断言,即允许在实例属性和变量声明后面放置一个!号,以告诉TS该属性会被明确赋值。文章源自略懂百科-http://wswcn.cn/108198.html

letnum:number;letnum1!:number;constsetNumber =()=>num =7constsetNumber1 =()=>num1 =7setNumber()
setNumber1()console.log(num)// errorconsole.log(num1)// ok复制代码文章源自略懂百科-http://wswcn.cn/108198.html

双重断言

断言失效后,可能会用到,但一般情况下不会使用文章源自略懂百科-http://wswcn.cn/108198.html

失效的情况:基础类型不能断言为接口文章源自略懂百科-http://wswcn.cn/108198.html

interfaceInfo{
name:string;
age:number;
}constname =小杜杜asInfo;// error, 原因是不能把 string 类型断言为 一个接口constname1 =小杜杜asanyasInfo;//ok复制代码文章源自略懂百科-http://wswcn.cn/108198.html

类型守卫

类型守卫:是可执行运行时检查的一种表达式,用于确保该类型在一定的范围内文章源自略懂百科-http://wswcn.cn/108198.html

我个人的感觉是,类型守卫就是你可以设置多种类型,但我默认你是什么类型的意思文章源自略懂百科-http://wswcn.cn/108198.html

目前,常有的类型守卫共有4种:in关键字typeof关键字interfaceof关键字类型谓词(is)文章源自略懂百科-http://wswcn.cn/108198.html

in关键字

用于判断这个属性是那个里面的文章源自略懂百科-http://wswcn.cn/108198.html

interfaceInfo {
name:stringage:number}interfaceInfo1{
name:stringflage:true}constsetInfo =(data: Info | Info1) =>{if("age"indata){console.log(`我的名字是:${data.name},年龄是:${data.age}`)
}if("flage"indata){console.log(`我的名字是:${data.name},性别是:${data.flage}`)
}
}文章源自略懂百科-http://wswcn.cn/108198.html

setInfo({name:小杜杜, age:7})// "我的名字是:小杜杜,年龄是:7"setInfo({name:小杜杜, flage:true})// "我的名字是:小杜杜,性别是:true"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

typeof关键字

用于判断基本类型,如string | number等文章源自略懂百科-http://wswcn.cn/108198.html

constsetInfo =(data:number|string|undefined) =>{if(typeofdata ==="string"){console.log(`我的名字是:${data}`)
}if(typeofdata ==="number"){console.log(`我的年龄是:${data}`)
}if(typeofdata ==="undefined"){console.log(data)
}
}文章源自略懂百科-http://wswcn.cn/108198.html

setInfo(小杜杜)// "我的名字是:小杜杜"setInfo(7)// "我的年龄是:7"setInfo(undefined)// undefined"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

interfaceof关键字

用于判断一个实例是不是构造函数,或使用类的时候文章源自略懂百科-http://wswcn.cn/108198.html

className{name: string =小杜杜}classAgeextendsName{age: number =7}constsetInfo =(data: Name) =>{if(datainstanceofAge) {console.log(`我的年龄是${data.age}`);
}else{console.log(`我的名字是${data.name}`);
}
}文章源自略懂百科-http://wswcn.cn/108198.html

setInfo(newName())// "我的名字是小杜杜"setInfo(newAge())// "我的年龄是7"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

类型谓词(is)

functionisNumber(x: any):xisnumber{//默认传入的是number类型returntypeofx ==="number";
}console.log(isNumber(7))// trueconsole.log(isNumber(7))//falseconsole.log(isNumber(true))//false复制代码文章源自略懂百科-http://wswcn.cn/108198.html

两者的区别

通过上面的介绍,我们可以发现断言与类型守卫的概念非常相似,都是确定参数的类型,但断言更加霸道,它是直接告诉编辑器,这个参数就是这个类型,而类型守卫更像确定这个参数具体是什么类型。(个人理解,有不对的地方欢迎指出~)文章源自略懂百科-http://wswcn.cn/108198.html

类型别名、接口

类型别名(type)

类型别名:也就是type,用来给一个类型起个新名字文章源自略懂百科-http://wswcn.cn/108198.html

typeInfoProps =string|numberconstsetInfo =(data: InfoProps) =>{}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

接口(interface)

接口:在面向对象语言中表示行为抽象,也可以用来描述对象的形状。文章源自略懂百科-http://wswcn.cn/108198.html

使用interface关键字来定义接口文章源自略懂百科-http://wswcn.cn/108198.html

对象的形状

接口可以用来描述对象,主要可以包括以下数据:可读属性、只读属性、任意属性文章源自略懂百科-http://wswcn.cn/108198.html

可读属性:当我们定义一个接口时,我们的属性可能不需要全都要,这是就需要?来解决只读属性:用readonly修饰的属性为只读属性,意思是指允许定义,不允许之后进行更改任意属性:这个属性极为重要,它是可以用作就算没有定义,也可以使用,比如[data: string]: any。比如说我们对组件进行封装,而封装的那个组件并没有导出对应的类型,然而又想让他不报错,这时就可以使用任意属性interfaceProps {
a:string;
b:number;
c:boolean;
d?:number;// 可选属性readonly e:string;//只读属性[f:string]:any//任意属性}letres: Props = {
a:小杜杜,
b:7,
c:true,
e:Domesy,
d:1,// 有没有d都可以h:2// 任意属性,之前为定义过h}letres.e =hi// error, 原因是可读属性不允许更改复制代码文章源自略懂百科-http://wswcn.cn/108198.html

继承

继承:与类一样,接口也存在继承属性,也是使用extends字段文章源自略懂百科-http://wswcn.cn/108198.html

interfacenameProps {
name:string}interfacePropsextendsnameProps{
age:number}constres: Props = {
name:小杜杜,
age:7}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

函数类型接口

同时,可以定义函数和类,加new修饰的事,不加new的事函数文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {
(data:number):number}constinfo: Props =(number:number) =>number//可定义函数// 定义函数classA {
name:stringconstructor(name:string){this.name = name
}
}interfacePropsClass{new(name:string): A
}constinfo1 =(fun: PropsClass, name:string) =>newfun(name)constres = info1(A,"小杜杜")console.log(res.name)// "小杜杜"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

type 和 interface 的区别

通过上面的学习,我们发现类型别名和接口非常相似,可以说在大多数情况下,type与interface是等价的文章源自略懂百科-http://wswcn.cn/108198.html

但在一些特定的场景差距还是比较大的,接下来逐个来看看文章源自略懂百科-http://wswcn.cn/108198.html

基础数据类型

type和interface都可以定义对象函数type可以定义其他数据类型,如字符串、数字、元祖、联合类型等,而interface不行typeA =string// 基本类型typeB =string|number// 联合类型typeC = [number,string]// 元祖constdom =document.createElement("div");// dom元素typeD =typeofdom
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

扩展

interface可以扩展type,type也可以扩展为interface,但两者实现扩展的方式不同。文章源自略懂百科-http://wswcn.cn/108198.html

interface是通过extends来实现type是通过&来实现// interface 扩展 interfaceinterfaceA {
a:string}interfaceBextendsA {
b:number}constobj:B = { a:`小杜杜`, b:7}// type 扩展 typetypeC = { a:string}typeD = C & { b:number}constobj1:D = { a:`小杜杜`, b:7}// interface 扩展为 TypetypeE = { a:string}interfaceFextendsE { b:number}constobj2:F = { a:`小杜杜`, b:7}// type 扩展为 interfaceinterfaceG { a:string}typeH = G & {b:number}constobj3:H = { a:`小杜杜`, b:7}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

重复定义

interface可以多次被定义,并且会进行合并,但type不行文章源自略懂百科-http://wswcn.cn/108198.html

interfaceA {
a:string}interfaceA {
b:number}constobj:A = { a:`小杜杜`, b:7}typeB = { a:string}typeB = { b:number}// error复制代码文章源自略懂百科-http://wswcn.cn/108198.html

联合类型(Union Types)

联合类型(Union Types): 表示取值可以为多种类型中的一种,未赋值时联合类型上只能访问两个类型共有的属性和方法,如:文章源自略懂百科-http://wswcn.cn/108198.html

constsetInfo =(name:string|number) =>{}文章源自略懂百科-http://wswcn.cn/108198.html

setInfo(小杜杜)
setInfo(7)
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

从上面看setInfo接收一个name,而name可以接收string或number类型,那么这个参数便是联合类型文章源自略懂百科-http://wswcn.cn/108198.html

可辨识联合

可辨识联合:包含三个特点,分别是可辨识、联合类型、类型守卫,文章源自略懂百科-http://wswcn.cn/108198.html

这种类型的本质是:结合联合类型字面量类型的一种类型保护方法。文章源自略懂百科-http://wswcn.cn/108198.html

如果一个类型是多个类型的联合类型,且多个类型含有一个公共属性,那么就可以利用这个公共属性,来创建不同的类型保护区块。文章源自略懂百科-http://wswcn.cn/108198.html

也就是上面一起结合使用,这里写个小例子:文章源自略懂百科-http://wswcn.cn/108198.html

interfaceA {type:1,
name:string}interfaceB {type:2age:number}interfaceC {type:3,
sex:boolean}// const setInfo = (data: A | B | C) => {// return data.type // ok 原因是 A 、B、C 都有 type属性// return data.age // error, 原因是没有判断具体是哪个类型,不能确定是A,还是B,或者是C// }constsetInfo1 =(data: A | B | C) =>{if(data.type ===1) {console.log(`我的名字是${data.name}`);
}elseif(data.type ===2){console.log(`我的年龄是${data.age}`);
}elseif(data.type ===3){console.log(`我的性别是${data.sex}`);
}
}文章源自略懂百科-http://wswcn.cn/108198.html

setInfo1({type:1, name:小杜杜})// "我的名字是小杜杜"setInfo1({type:2, age:7})// "我的年龄是7"setInfo1({type:3, sex:true})// "我的性别是true"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

定义了A、B、C三次接口,但这三个接口都包含type属性,那么type就是可辨识的属性,而其他属性只跟特性的接口相关。文章源自略懂百科-http://wswcn.cn/108198.html

然后通过可辨识属性type,才能使用其相关的属性文章源自略懂百科-http://wswcn.cn/108198.html

泛型

泛型:Generics,是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性文章源自略懂百科-http://wswcn.cn/108198.html

也就是说,泛型是允许同一个函数接受不同类型参数的一种模版,与any相比,使用泛型来创建可服用的组件要更好,因为泛型会保留参数类型(PS:泛型是整个TS的重点,也是难点,请多多注意~)文章源自略懂百科-http://wswcn.cn/108198.html

为什么需要泛型

我们先看看一个例子:文章源自略懂百科-http://wswcn.cn/108198.html

constcalcArray = (data:any):any[] => {letlist = []for(leti =0; i <3; i++){
list.push(data)
}returnlist
}console.log(calcArray(d))// ["d", "d", "d"]复制代码文章源自略懂百科-http://wswcn.cn/108198.html

上述的例子我们发现,在calcArray中传任何类型的参数,返回的数组都是any类型文章源自略懂百科-http://wswcn.cn/108198.html

由于我们不知道传入的数据是什么,所以返回的数据也为any的数组文章源自略懂百科-http://wswcn.cn/108198.html

但我们现在想要的效果是:无论我们传什么类型,都能返回对应的类型,针对这种情况怎么办?所以此时泛型就登场了文章源自略懂百科-http://wswcn.cn/108198.html

泛型语法

我们先用泛型对上面的例子进行改造下,文章源自略懂百科-http://wswcn.cn/108198.html

constcalcArray = (data:T):T[] => {letlist:T[] = []for(leti =0; i <3; i++){
list.push(data)
}returnlist
}constres:string[] = calcArray<string>(d)// okconstres1:number[] = calcArray<number>(7)// oktypeProps = {
name:string,
age:number}constres3: Props[] = calcArray({name:小杜杜, age:7})//ok复制代码文章源自略懂百科-http://wswcn.cn/108198.html

经过上面的案例,我们发现传入的字符串、数字、对象,都能返回对应的类型,从而达到我们的目的,接下来我们再看看泛型语法:文章源自略懂百科-http://wswcn.cn/108198.html

functionidentity<T>(value:T) :T{returnvalue
}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

第一次看到这个我们是不是很懵,实际上这个T就是传递的类型,从上述的例子来看,这个就是,要注意一点,这个实际上是可以省略的,因为 TS 具有类型推论,可以自己推断类型文章源自略懂百科-http://wswcn.cn/108198.html

多类型传参

我们有多个未知的类型占位,我们可以定义任何的字母来表示不同的参数类型文章源自略懂百科-http://wswcn.cn/108198.html

constcalcArray = (name:T, age:U): {name:T, age:U} => {constres: {name:T, age:U} = {name, age}returnres
}constres = calcArray<string,number>(小杜杜,7)console.log(res)// {"name": "小杜杜", "age": 7}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

泛型接口

定义接口的时候,我们也可以使用泛型文章源自略懂百科-http://wswcn.cn/108198.html

interfaceA<T>{data: T
}constInfo: A = {data:1}
console.log(Info.data)// "1"复制代码文章源自略懂百科-http://wswcn.cn/108198.html

泛型类

同样泛型也可以定义类文章源自略懂百科-http://wswcn.cn/108198.html

classclacArray{privatearr: T[] = [];文章源自略懂百科-http://wswcn.cn/108198.html

add(value: T) {this.arr.push(value)
}
getValue(): T {
let res =this.arr[0];
console.log(this.arr)returnres;
}
}constres =newclacArray()文章源自略懂百科-http://wswcn.cn/108198.html

res.add(1)
res.add(2)
res.add(3)文章源自略懂百科-http://wswcn.cn/108198.html

res.getValue()//[1, 2, 3]console.log(res.getValue)// 1复制代码文章源自略懂百科-http://wswcn.cn/108198.html

泛型类型别名

typeInfo = {
name?: T
age?: T
}constres:Info<string> = { name:小杜杜}constres1:Info<number> = { age:7}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

泛型默认参数

所谓默认参数,是指定类型,如默认值一样,从实际值参数中也无法推断出类型时,这个默认类型就会起作用。文章源自略懂百科-http://wswcn.cn/108198.html

constcalcArray = " {="" letlist:t[]="[]for(let" i="0;" <3;="" i++){list.push(data)="" }returnlist}="" 复制代码泛型常用字母文章源自略懂百科-http://wswcn.cn/108198.html

用常用的字母来表示一些变量的代表:文章源自略懂百科-http://wswcn.cn/108198.html

T:代表Type,定义泛型时通常用作第一个类型变量名称K:代表Key,表示对象中的键类型V:代表Value,表示对象中的值类型E:代表Element,表示的元素类型文章源自略懂百科-http://wswcn.cn/108198.html

常用技巧

在 TS 中有许多关键字和工具类型,在使用上,需要注意泛型上的应用,有的时候结合起来可能就有一定的问题文章源自略懂百科-http://wswcn.cn/108198.html

在此特别需要注意extends、typeof、Partial、Record、Exclude、Omit这几个工具类型文章源自略懂百科-http://wswcn.cn/108198.html

extends

extends:检验是否拥有其属性 在这里,举个例子,我们知道字符串和数组拥有length属性,但number没有这个属性。文章源自略懂百科-http://wswcn.cn/108198.html

constcalcArray = (data:T): number => {returndata.length// error}
复制代码文章源自略懂百科-http://wswcn.cn/108198.html

上述的calcArray的作用只是获取data的数量,但此时在TS中会报错,这是因为TS不确定传来的属性是否具备length这个属性,毕竟每个属性都不可能完全相同文章源自略懂百科-http://wswcn.cn/108198.html

那么这时该怎么解决呢?文章源自略懂百科-http://wswcn.cn/108198.html

我们已经确定,要拿到传过来数据的length,也就是说传过来的属性必须具备length这个属性,如果没有,则不让他调用这个方法。文章源自略懂百科-http://wswcn.cn/108198.html

换句话说,calcArray需要具备检验属性的功能,对于上述例子就是检验是否有length的功能,这是我们就需要extends这个属性帮我们去鉴定:文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {
length:number}constcalcArray = 可以看出calcArray(2)会报错,这是因为number类型并不具备length这个属性文章源自略懂百科-http://wswcn.cn/108198.html

typeof

typeof关键字:我们在类型保护的时候讲解了typeof的作用,除此之外,这个关键字还可以实现推出类型,如下图,可以推断中Props 包含的类型文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

keyof

keyof关键字: 可以获取一个对象接口的所有key值,可以检查对象上的键是否存在文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {
name:string;
age:number;
sex:boolean}typePropsKey = keyof Props;//包含 name, age, sexconstres:PropsKey =name// okconstres1:PropsKey =tel// error// 泛型中的应用constgetInfo = 索引访问操作符文章源自略懂百科-http://wswcn.cn/108198.html

索引访问操作符:通过[]操作符可进行索引访问,可以访问其中一个属性文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

in

in:映射类型, 用来映射遍历枚举类型文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

infer

infer:可以是使用为条件语句,可以用infer声明一个类型变量并且对它进行使用。如文章源自略懂百科-http://wswcn.cn/108198.html

typeInfo = Textends{ a: infer U; b: infer U } ? U : never;typeProps = Info<{ a:string; b:number}>;// Props类:string | numbertypeProps1 = Info<number>// Props类型:never复制代码文章源自略懂百科-http://wswcn.cn/108198.html

Partial

Partial语法:Partial作用:将所有属性变为可选的?文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {name:string,age:number}constinfo: Props = {name:小杜杜,age:7}constinfo1: Partial = {name:小杜杜}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

从上述代码上来看,name 和 age 属于必填,对于 info 来说必须要设置 name 和 age 属性才行,但对于 info1来说,只要是个对象就可以,至于是否有name、 age属性并不重要文章源自略懂百科-http://wswcn.cn/108198.html

Required

Required语法:Required作用:将所有属性变为必选的,与Partial相反文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps{name:string,age:number,sex?:boolean}const info:Props={name:小杜杜,age:7}const info1:Required={name:小杜杜,age:7,sex:true}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

Readonly

Readonly语法:Readonly作用:将所有属性都加上 readonly 修饰符来实现。也就是说无法修改文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {
name:stringage:number}letinfo: Readonly = {
name:小杜杜,
age:7}文章源自略懂百科-http://wswcn.cn/108198.html

info.age =1//error read-only 只读属性复制代码文章源自略懂百科-http://wswcn.cn/108198.html

从上述代码上来看,Readonly修饰后,属性无法再次更改,智能使用文章源自略懂百科-http://wswcn.cn/108198.html

Record

Record语法:Record文章源自略懂百科-http://wswcn.cn/108198.html

作用:将K中所有的属性的值转化为T类型。文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {name:string,age:number}typeInfoProps = JS | TSconstInfo: Record = {JS:{name:小杜杜,age:7},TS:{name:TypeScript,age:11}}复制代码文章源自略懂百科-http://wswcn.cn/108198.html

从上述代码上来看,InfoProps的属性分别包含Props的属性文章源自略懂百科-http://wswcn.cn/108198.html

需要注意的一点是:K extends keyof any其类型可以是:string、number、symbol文章源自略懂百科-http://wswcn.cn/108198.html

Pick

Pick语法:Pick文章源自略懂百科-http://wswcn.cn/108198.html

作用:将某个类型中的子属性挑出来,变成包含这个类型部分属性的子类型。文章源自略懂百科-http://wswcn.cn/108198.html

interfaceProps {
name:string,
age:number,
sex:boolean}typenameProps = Pick从上述代码上来看,Props原本属性包括name、age、sex三个属性,通过Pick我们吧name和age挑了出来,所以不需要sex属性文章源自略懂百科-http://wswcn.cn/108198.html

Exclude

Exclude语法:Exclude文章源自略懂百科-http://wswcn.cn/108198.html

作用:将T类型中的U类型剔除。文章源自略懂百科-http://wswcn.cn/108198.html

// 数字类型typenumProps = Exclude<1|2|3,1|2>// 3typenumProps1 = Exclude<1,1|2>// nervertypenumProps2 = Exclude<1,1>// nervertypenumProps3 = Exclude<1|2,7>// 1 2// 字符串类型typeinfo ="name"|"age"|"sex"typeinfo1 ="name"|"age"typeinfoProps = Exclude// "sex"// 类型typetypeProps = Exclude<string|number|(() =>void),Function> //string|number// 对象typeobj= {name: 1,sex:true}typeobj1= {name: 1 }typeobjProps=Exclude<obj,obj1> //nerver复制代码文章源自略懂百科-http://wswcn.cn/108198.html

从上述代码上来看,我们比较了下类型上的,当 T 中有 U 就会剔除对应的属性,如果 U 中又的属性 T 中没有,或 T 和 U 刚好一样的情况都会返回nerver,且对象永远返回nerver文章源自略懂百科-http://wswcn.cn/108198.html

Extra

Extra语法:Extra文章源自略懂百科-http://wswcn.cn/108198.html

作用:将T 可分配给的类型中提取 U。与Exclude相反文章源自略懂百科-http://wswcn.cn/108198.html

typenumProps=Extract<1|2|3,1|2>//1|2复制代码文章源自略懂百科-http://wswcn.cn/108198.html

Omit

Omit语法:Omit文章源自略懂百科-http://wswcn.cn/108198.html

作用:将已经声明的类型进行属性剔除获得新类型文章源自略懂百科-http://wswcn.cn/108198.html

image.png文章源自略懂百科-http://wswcn.cn/108198.html

Exclude的区别:Omit 返回的是新的类型,原理上是在Exclude之上进行的,Exclude是根据自类型返回的文章源自略懂百科-http://wswcn.cn/108198.html

NonNullable

NonNullable语法:NonNullable作用:从 T 中排除null和undefined文章源自略懂百科-http://wswcn.cn/108198.html

ReturnType

ReturnType语法:ReturnType文章源自略懂百科-http://wswcn.cn/108198.html

作用:用于获取函数T的返回类型。文章源自略懂百科-http://wswcn.cn/108198.html

typeProps = ReturnType<()=>string>// stringtypeProps1 = ReturnType<从上述代码上来看, ReturnType可以接受 any 和 never 类型,原因是这两个类型属于顶级类型,包含函数文章源自略懂百科-http://wswcn.cn/108198.html

Parameters

Parameters:Parameters作用:用于获取获取函数类型的参数类型文章源自略懂百科-http://wswcn.cn/108198.html

typeProps = Parameters<()=>string>// []typeProps1 = Parameters<(data:string) =>void>// [string]typeProps2 = Parameters<any>;// unknown[]typeProps3 = Parameters;// never复制代码文章源自略懂百科-http://wswcn.cn/108198.html

End

参考:

TypeScript 4.0[3]深入理解 TypeScript[4]一份不可多得的 TS 学习指南(1.8W字)[5]文章源自略懂百科-http://wswcn.cn/108198.html

以及网上的各种各样的资源。文章源自略懂百科-http://wswcn.cn/108198.html

小结

到此,有关TS的知识就已经说完了,相信掌握了这些知识,你一定会对TS有更深的理解,这篇文章按照自己的理解,进行分类,个人觉得这样的分类比较合理,如果有什么更好的建议,欢迎在评论区指出~文章源自略懂百科-http://wswcn.cn/108198.html

想到自己刚接触TS的时候,是有点抵触的,但随着时间的推移,发现TS真的很香,并且TS也不算是很难,只要你花费一定的时间,在结合与项目,你就会发现真香定律文章源自略懂百科-http://wswcn.cn/108198.html

相信这篇文章已经极大程度的解决了TS相关的代码,希望这篇文章能让你迅速掌握TS,喜欢的点个赞支持下吧(● ̄(エ) ̄●)文章源自略懂百科-http://wswcn.cn/108198.html


文章源自略懂百科-http://wswcn.cn/108198.html 文章源自略懂百科-http://wswcn.cn/108198.html

文章源自略懂百科-http://wswcn.cn/108198.html

懵懂先生
  • 本文由 发表于 2023年4月11日 18:31:20
  • 转载请注明:http://wswcn.cn/108198.html
投稿文章

最好的床垫(什么样床垫舒适又护腰)

中老年人随着年龄的增长,身体机能大不如从前,睡眠也开始出现问题,主要表现为:睡眠时长变短,睡眠浅,醒得早。那么对于中老年人来说,怎样做才能改善睡眠呢? 睡眠障碍在中老年人群中很常见,很多情况是由于床垫...
投稿文章

10月31日最后一天说说

1.我若在你心上,情敌三千又何妨,你若在我身旁,负尽天下又怎样,你若一直在,我便一直爱,你若不在,我便封心锁爱! 2.生活并不完美,但并不代表它不美,不要去羡慕别人的表面风光,其实每个人都有自己内心的...
投稿文章

亲戚离得远有必要人情来往么 - 亲戚的亲戚

春节到了,走亲访友大拜年这是习俗,也是必不可少的事情。最近刷到一些网友为此而发的帖子: 有位网友说:拿几样破东西来,我还要做一桌菜好好招待,太烦! 有位网友说:春节假期本想好好休息陪陪家人,可是却要天...
投稿文章

很无聊怎么办(一个人特别无聊该怎么办)

家长百问百答 场景:孩子说没事可做,对自己的所有玩具或游戏都不感兴趣。 孩子通常总是迸发着热情,所以当孩子说他无聊时,你可能会担心自己没有给孩子提供足够的刺激性活动。 无聊有时候是件好事——这标志着你...