导读
类型声明、函数返回值类型声明、基本类型、联合类型、字面量、unknow未知类型、object类型、Array类型、tuple元组、类型别名、编译选项、enum枚举、构造函数constrctor、继承 、接口、抽象类、属性的封装、泛型。函数的重载、类型断言as、非空断言!、可选参数?、可选链?.、??空值合并操作符、never类型
类型
类型声明
变量声明
//声明一个变量a,同时指定它的类型为number let a:number; a=10; a='wxl' //会报错,因为a在使用的过程中值只能是数字 //声明完变量直接进行赋值 let a:number = 10; //当然,变量的声明和赋值可以同时进行,然后ts可以自动对变量进行类型检测, //后续这个变量所有的赋值操作都是基于第一次类型检测来进行的。 let c = true; c = false;
函数类型声明
//接收数字类型的参数 //函数的返回类型是number function sum(a:number,b:number) :number{ return a+b; } let result = sum(1,2) //设置函数结构的类型声明 let d:(a:number,b:number)=>number let m:()=>void //void在这里代表是任意类型
当函数有默认值的时候,尽量放在最后。
函数传值时建议顺序:(必传参数,有默认值的参数,可选参数)
//会报错 function fn(x:number=10,y:number){ } // fn(20) 报错 fn(undefined,20) //不会报错 //不会报错 function fn2(x:number,y:number=10){ } fn2(20)
函数的重载
名称相同,但参数或参数类型不同,就是函数重载。
// 定义函数 function add(num1:number,num2:number):number; //还没有函数体实现,只是先定义 function add(num1:string,num2:string):string; function add(num1:string,num2:number):string; function add(num1:number,num2:string):string; //函数实现 function add(num1:any,num2:any):any{ return num1+num2 } console.log(add(1,2)) //3 console.log(add('a','b')) //ab console.log(add('a',1))//a1 console.log(add(1,'a'))//1a
基本类型
联合类型
let c: boolean | string; c=true c='wxl' //联合类型经常和type一起使用 //type a: string | number
联合类型在代码提示上是全部类型的整合,不是联合类型中其中某一个,比如上边代码中的c的类型就是’boolean | string’而不是单一的boolean或者string。
函数重载和联合类型都能解决函数参数不确定的需求,但是优先使用联合类型,只有在返回值不确定的情况下更适合函数重载。
字面量
const message='zzl' //检测后,相当于 const message:'zzl'='zzl' let message2 = 'wxl' //检测到message2的类型却是string
字面量的意义就是必须结合联合类型
let b: 2 | 3; b=2; b=3; b=100;//此时会报错,b只能在2和3之间赋值
null、undefined类型
let n:null=null //null类型只有一个值,就是null let n2:undefined=undefined
unknow未知类型
//表示未知类型的值(即可以是任意类型) let e:unknown; e=10; e='wxl' let y:string; y=e;//此时会报错,虽然都是string类型,但是e定义的类型是unknow, //但是如果用相同效果但不安全的any类型,此时就不会报错了 //因为,unknow类型只能 赋值给 any和unknown类型,防止滥用 //any类型可以赋值给任意类型 //解决办法 //方法1 if(typeof e === 'string'){ y=e; //此时就不会报错了,因为这里使用了类型保护 } //方法2 (类型断言) y = e as string y = <string> e;
常见的类型保护
- typeof
- 平等缩小(比如===、!==)
- instanceof 是否是它的实例
- in
- 等等
in的使用
type Fish={ swim:()=>void } type Dog={ run:()=>void } function test(animal:Fish|Dog){ if('swim' in animal){ animal.swim() }else{ animal.run() } } const f:Fish={ swim(){ console.log('鱼在游') } } test(f)
类型断言as
const el = document.getElementById('d') as HTMLImageElement const el2 = document.querySelector('img') //自动HTMLImageElement类型 //只有HTMLImageElement类型的变量才能取到img的src属性 el.src='。。。'
非空断言!
跳过ts在编译阶段对变量的检测。非空断言不能滥用,不安全,建议使用可选链。
function print(message?:string){ // console.log(message.length) 会报错 // if(message){ // console.log(message.length) // } console.log(message.length!) // ! 表示我肯定message有值 } print('aaa')
可选参数?
function test2(a:number,b?:string){} //可选参数要放在必选类型后边 //属性名后加?,表示这个属性是可选的,它相当于 a:undefined | number 即联合类型
可选链?. (es11的特性)
它的作用是当对象的属性不存在时,会短路,即直接返回undefined;如果存在,那么才会继续执行。
type person={ name:string, friend?:{ name:string, age?:number } } const info:person={ name:'zzl', friend:{ name:'wxl' } } console.log(info.name) //zzl // friend存在的话就去拿它的name,不存在就返回undefined console.log(info.friend?.name) //wxl console.log(info.friend?.age) //undefined 因为拿第一层拿不到的时候返回就是undefined,只有undefined.age才是报错的原因 const info2:person={ name:'zzl', } console.log(info2.friend?.name) //undefined
??空值合并操作符
let message:string|null = null const content = message ?? '我是默认值' console.log(content) //我是默认值 ,因为message没有值 let message2:string|null = 'wxl' const content2 = message2 ?? '我是默认值' console.log(content2) //wxl
object类型
//属性名后加?,表示这个属性是可选的,它相当于 a:undefined | number 联合类型 let p:{name:string, age?:number} p = {name:'wxl'} [propName:string]:any 表示任意类型的属性,即必须有一个name属性,其他属性可有可无 let c:{name:string, [propName:string]:any}; c = {name:'wxl',age:18,gen:'男'};
Array类型
let g:number[] //推荐写法 let g:Array<number>; //react jsx中有冲突,<div></div> g = [1,2,3];
tuple元组
//元组就是多种元素的组合,且是固定长度的数组 let h: [string,number,number]; h = ['w',1,2];
enum枚举
enum Gender{ man=0, gril=1 } let i:{name:string,gender:Gender}; i={ name:'wx', gender:Gender.man } console.log(i.gender === Gender.man)
never类型
//当一个函数压根都没有返回值,此时就可以使用never function test():never{ throw new Error() }
.d.ts声明类型
不运行,单纯用做声明类型的
index.d.ts
//因为有时候导入的模块,作者自己没有写.d.ts,所以会报错,需要我们自己定义.d.ts,并声明它的类型) //所以需要使用,声明模块 declare module 'lodash' { export function join(arr: any[]): void } // 声明变量/函数/类 declare let zzlName: string declare function getName(): void declare class Person{ name:string age:number constructor(name:string,age:number) } //声明文件 declare module '*.jpg' //声明命名空间 declare namespace $ { export function ajax(url:string):string }
ts文件根据index.d.ts的类型声明来进行类型检测。,是会运行的(编译成js)
test.ts
import a from './a1.jpg' const p = new Person("why",19) console.log(p.name) $.ajax('127.0.0.1')
类型别名
type myType = boolean | string; let k:myType; k='d'
编译选项
在项目目录下创建一个tsconfig.json的文件,并在目录路径下的终端里运行 tsc命令,此时目录 下所有的ts文件都会编译成Js文件。
{ //所有严格检查的总开关 "strict":true, // include用来指定哪些文件可以被编译 **表示任意目录 *表示任意文件 "include": [ "./src/**/*" ], // 定义需要排除在外的目录 // 默认值:["node_modules"......] "exclude": [ ], "compilerOptions": { "target": "ES6" }, //用来指定编译后的js文件所在目录 "outDir":"./dist", //是否对js文件进行编译,这样也会输出到之前设置的目录里 "allowJss":true, //是否检查js代码是否符合ts规范 "checkJs":true, //是否移除注释 "removeComments":true, //当有错误的时候,就不生成编译文件 "noEmitOnError":true, }
class
构造函数constrctor
class Dog{ name:string; constructor(name:string){ // 在构造函数中this就说是当前新建的那个对象 this.name=name; } say(){ // this是当前调用方法的对象,即表示当前对象 console.log(this) } } const dog = new Dog('ww'); const dog2 = new Dog('xx'); console.log(dog) console.log(dog2) dog.say();
继承
class Animal{ name:string; constructor(name:string){ this.name=name; } sayHello(){ console.log('我叫了 ') } } class Dog extends Animal{ } class Cat extends Animal{ run(){ console.log('猫在跑') } sayHello(){ console.log('我要独特的叫') } } const dog = new Dog('旺财'); const cat = new Cat('加菲猫') dog.sayHello() //我叫了 cat.run() //猫在跑 cat.sayHello() //我要独特的叫
super 父类
class Animal{ name:string; constructor(name:string){ this.name=name; } sayHello(){ console.log('我叫了 ') } } class Cat extends Animal{ age:number; //当重写父类的构造函数时, constructor(name:string,age:number){ //调用父类的构造函数,此时就有name这个属性了。 super(name) this.age=age } run(){ console.log('猫在跑') } //重写sayHello sayHello(){ console.log('我要独特的叫') //当然也可以继续调用父类的方法,super.sayHello() } } const cat = new Cat('加菲猫',12) cat.run() cat.sayHello()
访问器setter与getter
class Person{ private _name:string constructor(name:string){ this._name=name } set name(newName){ this._name=newName } get name(){ return this._name } } const p = new Person('zzl') p.name='wxl' console.log(p.name)
抽象类
- 抽象类不能用来创建对象
- 抽象类就是专门用来 被继承的类
abstract class Animal{ name:string; constructor(name:string){ this.name=name } //定一个抽象方法 abstract sayHello():void } class Cat extends Animal{ sayHello(){ console.log('我要独特的叫',this) } } const cat = new Cat('加菲猫') cat.sayHello()
接口
- 接口用来定义一个类结构,用来 定义一个类中应该包含哪些属性和方法
- 同时 接口也可以当成type x 类型别名声明来使用。
- 接口中所有的属性都不能有实际的值
- 接口中所有的方法都是抽象方法
interface myInterface<T=string>{ //泛型给一个默认值 name:string; sayHello():void; action?:(value:T)=>T } //等同于 type myInterface {name:string,age:number} // 定义类时,可以使类去实现 一个接口 class A implements myInterface{ name: string; constructor(name:string){ this.name=name } sayHello(): void { } } const a = new A('wxl')
索引类型
// 通过interface来定义索引类型 interface IndexTest{ [index:number]:string } const a:IndexTest={ 0:'zzl', 1:'wxl' } interface IndexTest2{ [name:string]:number } const a2:IndexTest2={ 'age':1, 'height':150 }
属性的封装
- public. 修饰的属性可以在任意位置访问和修改
- private 私有属性,只能在类的内部进行访问(修改)
- 可以通过添加 方法使得私有属性可以被外部访问
- protected 受保护的属性,只能在当前类和当前类的子类中访问(修改)
- readonly 只读属性,可以在构造器中赋值,且赋值之后就不可以修改了。
- 属性本身不能进行修改,但是如果它是对象类型,则对象中的属性是可以修改的
class Person{ private _name:string; private _age:number constructor(name:string,age:number){ this._name=name this._age=age } get name(){ return this._name } set name(value:string){ //可以判断将要赋值是否符合要求 if(value.length <= 3){ this._name=value }else{ console.log('名字太长,不允许修改') } } get age(){ return this._age } set age(value:number){ this._age=value } } const per = new Person('wxl',10) // per._name='ll';//不允许这样访问,会报错 per.name='hhh' console.log(per.name) per.age=100 console.log(per.age) /////////////////////////// //也可以直接将属性定义在构造函数中,和上面类的构造函数一样 class C{ constructor(public name:string,public age:number){} } const c = new C('xxx',10) console.log(c)
泛型
在定义函数或类时,如果遇到类型不明确就可以使用泛型,即类型的参数化,让外界决定类型。
function fn<T>(a:T):T{ return a; } //可以直接调用具有泛型的函数 fn<number>(10); //此时T就是number fn<string>('wxl'); //此时T就是String //////////////// interface Inter{ //表示对象里要有length属性 length:number } //interface I2<T1,T2=string>{ // name:T1, // age:T2 //} //T extends Inter 表示传的泛型必须是Inter的实现类,即实现了类型的类型约束 function fn2< T extends Inter>(a: T):number{ return a.length } // 'wxl'有length属性 fn2('wxl') // fn2(10) //此时会报错,因为10没有length属性
编译环境
node下的编译环境
npm install ts-node -g npm install tslib @types/node -g
ts-node index.ts #此时就可以编译了