数据类型 | 关键字/描述 | 用法 |
---|---|---|
数字类型 | number | let decLiteral: number = 6 |
布尔类型 | boolean | let flag: boolean = true; |
字符串类型 | string | let name: string = “Runoob” |
数组类型 | array | // 在元素类型后面加上[] let arr: number[] = [1, 2]; // 使用数组泛型 let arr: Array = [1, 2]; |
元组 | 固定元素数量和类型,类型可不一致, 但位置必须一样 | let x: [string, number]; x = [‘Runoob’, 1]; // 运行正常 x = [1, ‘Runoob’]; // 报错 console.log(x[0]); // 输出 Runoob |
枚举 | enum/定义数值集合 | enum Color {Red, Green, Blue}; let c: Color = Color.Blue; console.log©; // 输出 2 |
void | void/表示该方法没有返回值 | function hello(): void { //没有return 返回值 } |
null | null/对象值缺失 | let n: null = null; |
undefined | undefined/未定义的值 | let u: undefined = undefined; |
never | never/never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值 | // 返回never的函数必须存在无法达到的终点 function error(message: string): never { throw new Error(message); } |
unknown | unknown/unknown类型只能赋值给any类型和unknown类型 | let value: unknown; let value1: unknown = value; // OK let value2: any = value; // OK let value3: boolean = value; // Error |
映射对应数据
1. 数字枚举:初始值可自动增长
实际场景:文章分类有固定的id 这时候就可以使用到枚举去定义
enum Category {<!-- --> Work,//这里是有默认初始值 为 0 依次增长 Life, Study, } let cateGoryId: Category =Category.Study console.log(cateGoryId)// 2
2.字符串枚举
定义的成员必须都是字符串
比如每个人会取自己的英文名,对应的英文名和真实人名,可以去做一一映射
enum Person {<!-- --> jack = '小王', role = '小蓝', Anna = '小红', } let person: Person =Person.Anna console.log(person)//小红
3.异构枚举
异:就是里面成员既有数字又有字符串,大杂炖
enum mess {<!-- --> jack = '小王', role = '小蓝', Anna = '小红', c = 1, b = 2 }
类型断言必须联合类型中的一种,类型断言只是做类型选择,而不是做类型转换
编译阶段起作用
在定义一个变量为联合类型时,要去做判断会遇到以下的情况
const unionGetLength2 = (something: string | number): number => {<!-- --> if(something.length){<!-- --> //报错something无法去判断到底时哪个类型 return something.length; } else {<!-- --> return something.toString().length; } }
可以用断言去处理上面这个类型不能判断的问题,断言有两种写法:
const assertionGetLength = (something: string | number): number => {<!-- --> if((something as string).length){<!-- -->//告诉TS something 就是字符串类型 return (something as string).length; } else {<!-- --> return something.toString().length; } }
const assertionGetLengthOther = (something: string | number): number => {<!-- --> if((<string>something).length){<!-- --> 告诉ts something就是字符串类型 return (<string>something).length; } else {<!-- --> return something.toString().length; } }
当遇到条件语句时,会 ** 限制变量类型
function test(own: string | boolean | number) {<!-- --> if (typeof own == 'string') {<!-- --> // 这里 own 的类型限制为 string } else if (typeof own == 'number') {<!-- --> // 这里 own 的类型限制为 number } else {<!-- --> // 这里 own 的类型限制为 boolean } }
interface one {<!-- --> name: string; speak:string; } interface two {<!-- --> age: number; see:string; } function test(own:one | two){<!-- --> console.log("Name: " + own.name); if ("name" in own) {<!-- --> //这里限制为own 对象为one console.log(own.speak); } if ("see" in own) {<!-- --> //这里限制为own限制的对象为two console.log(own.see); } }
interface Padder {<!-- --> getPaddingString():string } class Space implements Padder {<!-- --> constructor(private numSpaces: number) {<!-- -->} getPaddingString() {<!-- --> return Array(this.numSpaces + 1).join(' '); } } class StringPadder implements Padder {<!-- --> constructor(private value: string) {<!-- -->} getPaddingString() {<!-- --> return this.value; } } function getrandom() {<!-- --> return Math.random() < 0.5 ? new Space(4) : new StringPadder(''); } let padder: Padder = getrandom(); //判断padder是否是Space的实例对象,如果中间有其他值覆盖了,会出现问题 if (padder instanceof Space) {<!-- --> //判断后,确保这个值是它的实例对象 padder类型收缩在'SpaceRepeatingPadder' }
返回布尔值条件函数
function isString (own: any): own is string {<!-- --> return typeof own === 'string'; } function test (xdom:any) {<!-- --> if (isString(xdom)) {<!-- --> //xdom 限制为 'string' } else {<!-- --> //其他类型 } }
取值可以为多种类型中的一种
特点:1.使用的属性和方法需要类型共有的
2.通常与 null 或 undefined 一起使用:
let stringAndNumber: string | number;
类型另一个外号,用type定义
不用类型别名: let greet = (message: string | string[]) => {<!-- --> // ... }; 用类型别名: type Message = string | string[]; let greet = (message: Message) => {<!-- --> // ... };
同时满足多个类型,多种类型叠加成为一种类型,要包含所需所有类型的特性
interface IPerson {<!-- --> id: string; age: number; } interface IWorker {<!-- --> companyId: string; } type IStaff = IPerson & IWorker;//定义IStaff类型 同时满足这两个接口类型条件 const staff: IStaff = {<!-- --> //staff我i IStaff类型 必须一起满足才可 id: 'E1006', age: 33, companyId: 'EFT' }; console.dir(staff)
TypeScript | JavaScript |
---|---|
含类型 | 无类型 |
箭头函数 | 箭头函数(ES2015) |
函数类型 | 无函数类型 |
必填和可选参数 | 所有参数都是可选的 |
默认参数 | 默认参数 |
剩余参数 | 剩余参数 |
函数重载 | 函数重载 |
可以定义函数的参数类型和返回值
function createUserId(name:string,id:number) {<!-- --> return name + id }
let createUserId:(name:string,id:number)=>string
?表示这个参数是可选的
注意:可选参数要放在普通参数后面,不然会导致编译错误
function createUserId(name:string,id:number,age?:number) {<!-- --> return name + id; }
默认name这个参数的值为Jons
function createUserId(name:string = 'Jons',id:number) {<!-- --> return name + id; }
简写,可以把剩余后面写的那些参数一起表示
function push(array,...items) {<!-- --> console.log(items) } push(a,1,2,3) //items是个数组 [1,2,3]
常见的根据参数的类型执行不同的函数
多数用于传入不同的参数得到不同的结果
重载分两个部分:1.声明 2.实现 缺一不可
例1:
例如我们有一个add函数,可以接收string 类型相拼接,也可以接收number类型,相加
//声明: function add (arg1: string, arg2: string): string function add (arg1: number, arg2: number): number //实现, function add (arg1: string | number, arg2: string | number) {<!-- --> // 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 arg1 + arg2 if (typeof arg1 === 'string' && typeof arg2 === 'string') {<!-- --> return arg1 + arg2 } else if (typeof arg1 === 'number' && typeof arg2 === 'number') {<!-- --> return arg1 + arg2 } }
注意:如果实现部分没有严格判断,会提示此重载签名与其实现签名不兼容。函数重载只是多个函数声明,逻辑依旧要自己写,并不会把我们声明的多个函数进行合并
例子2 :
写个函数test,当传入参数 param 是User时,不传入flag,当传入的param时number时,传入flag
下面看看不用重载:
interface User {<!-- --> name: string; age: number; } declare function test(param: User | number, flag?: boolean): number; const user={<!-- --> name:'Jack', age:18 } const res=test(user,false) //虽然不报错,但是与想法违背 当传入的是User是 此时应该不让传入flag
重载解决上诉问题:
interface User{<!-- --> name:string; age:number; } declare function test(params:User):number; declare function test(params:number,flag:boolean):number; const user={<!-- --> name:'jack', age:666 }; //类型“{ name: string; age: number; }”的参数不能赋给类型“number”的参数。ts(2345) const res=test(user,false);
实际项目中的用法:
有声明之后,需要有具体的实现
interface User{<!-- --> name:string; age: number; } const user:User ={<!-- --> name:'jack', age: 123 } class oneClass {<!-- --> //声明 public test(param: User): number; public test(param: number, flag: boolean): number; //实现 public test(param: User | number, flag?:boolean) {<!-- --> if (typeof param==='number') {<!-- --> return param+(flag?1:0) }else{<!-- --> return param.age } } }
如果传入的参数不同,但是得到的结果是一样的,那么重载就意义不大了
重载个人觉得比较繁琐,一些可以用其他方法代替重载,反而更加的简单
例如:
用可选参数来代替
function func (a: number): number function func (a: number, b: number): number //代替上面: function func (a: number, b?: number): number
用联合类型来代替:
function func (a: number): number function func (a: string): number //替代上面 function func (a: number | string): number
ts在这个过程是类型校验,如果定义该类型,与后面数组对应数据冲突,会提示
//不能将类型 “ number ” 分配给类型 “ string ” let x: number; let y: number; let z: string; let five_array = [0, 1, 2, 3, 4]; [x, y, z] = five_array;
定义一个只包含数字的数组:
const arr: number[] = [1,2,3]
既存数字又存字符串:
const arr1: (number | string)[] = [1, '2' ,3]
const objectArr: {<!-- -->name: string, age: number}[] = [{<!-- -->name:'a', age:16}]
元组本质上也是数组,所以数组上的操作元组也可以去使用 一个数组里面的长度和类型都是固定的时候,就可以使用元祖
const teacherinfo2: [string, string, number] = ['zina', 'girl', 18]; //这里是固定长度以及固定了每一个的类型,如果类型不一样会提示
接口来定义对象的类型
1.首字母大写
2.变量的形状必须和接口的形状保持一致
举个栗子:
interface Person {<!-- --> name:string age:number } let jack:Person = {<!-- --> //变量的类型必须和上面是保持一致的 name:'jack', age:18 }
和数组一样,对象里面的属性是可选的,我们经常需要不一样形状的对象
interface Person {<!-- --> name:string age?:number } let jack:Person = {<!-- --> name:'jack' // age 可以不写 }
用于不确定属性的数量的场景
确定属性和可选属性都必须符合任意属性的类型
如果不符合如下:
interface Person {<!-- --> name: string; age?: number; [propName: string]: string; } //类型“number”的属性“age”不能赋给字符串索引类型“string”。
正确写法:
interface Person {<!-- --> name:string; age?:number; [proppName:string]:any; }
有些字段只能在创建的时候被赋值,其他时候不允许被更改,可以使用只读属性
interface Person {<!-- --> readonly id: number; name: string; age?: number; [propName: string]: any; } let tom: Person = {<!-- --> name: 'Tom', gender: 'male' }; tom.id = 89757; //无法分配到 "id" ,因为它是只读属性
类型不明确,但是要求其中一部分或者全部类型是一致的情况
在不知道变量类型的情况下定义: 如果要求first输入的类型,second必须一样 这时候下面这个代码就不能做到了
可以用泛型解决这个的问题
function test(first:any,second:any){<!-- --> return `${<!-- -->first}+${<!-- -->second}` }
泛型解决:T为自定义的,取ABC都可以
function test<T>(first:<T>,second:<T>){<!-- --> return `${<!-- -->first}+${<!-- -->second}` } test<number>(1,1) test<number>(1,'1')//报错
可以定义一个数组里面的类型
function map<T>(params:T[]){<!-- --> return params } 另一个写法 function map<T>(params:Array<T>){<!-- --> return params } map<string>([1,'1'])//报错 map<string>(['1','1'])
定义多个泛型:
function test<T,P>(first:T,second:P):T{<!-- --> return first //要求返回值和first 的类型一致 } test<string,number>('1',1) test('1',1) //不定义类型也可以,ts会自动推断出该T是string类型和P是number类型
class Person {<!-- --> name = 'dell' getName(){<!-- --> return this.name; } } const person = new Person(); console.log(person.getName())//dell
class Person {<!-- --> name = 'dell' getName(){<!-- --> return this.name; } } class Teacher extends Person {<!-- --> getTeacherName(){<!-- --> return 'teacher' } } const teacher = new Teacher(); //声明的teacher实例里面 既有getName方法 又有getTeacherName console.log(teacher.getName()); //dell console.log(teacher.getTeacherName()) //teacher
在继承了父类方法之后,可以子类方法里面去重写父类的方法
注意:覆盖了之后,还需调用父类的方法,可以用 super 去调用
class Person {<!-- --> name = 'dell' getName(){<!-- --> return this.name; } } class Teacher extends Person {<!-- --> getTeacherName() {<!-- --> return 'teacher'; } getName() {<!-- --> return 'lee'; } } console.log(teacher.getName());//lee 这个时候子类的getName把父类的getName给覆盖了 // 如果Teacher 写成如下 class Teacher extends Person {<!-- --> getTeacherName() {<!-- --> return 'teacher'; } getName() {<!-- --> // super.getName()//调用的是父类的方法 return 'lee' + super.getName(); } }
允许在类内被使用
首先明确什么是类内,什么是类外
class Person {<!-- --> private name = 'dell' getName(){<!-- --> console.log(this.name)//类内 可以被调用 } } const person = new Person() console.log(person.name) //这个是类外 报错
允许在类内及继承的子类中使用
class Person {<!-- --> protected name = 'dell' getName(){<!-- --> console.log(this.name)//类内 可以被调用 } } class Teacher extends Person {<!-- --> getTeacherName() {<!-- --> console.log(this.name)//继承了父类的name 允许被调用 } }
允许我在类内外被调用,不写的情况默认就是public
class Person {<!-- --> name = 'dell'//默认前面都是public name getName(){<!-- --> //默认前面都是public getName console.log(this.name) } } const person = new Person() console.log(person.name)
new 实例的时候 这个瞬间 constructor()被会马上执行
传统写法: class Person {<!-- --> public name:string constructor(name:string){<!-- --> this.name=name } } const person =new Person('dell') console.log(person.name) 可以简化为: class Person {<!-- --> constructor(public name: string) {<!-- --> } }
继承中父子构造器冲突的情况:
继承之后 如果父类有构造器 子类也有构造器的时候 子类必须调用父类的构造函数。还必须按照父类的构造器的要求把参数传入
class Person{<!-- --> constructor(public name:string){<!-- --> } } class Teacher extends Person{<!-- --> constructor(public age:number){<!-- --> super('dell') } } const teacher =new Teacher(28) console.log(teacher.age)//28 console.log(teacher.name)//dell
举个栗子:
假设某类有一个私有变量,但是又需要向外界去暴露这个私有变量,然后不能被外界知道这个变量的具体情况
可以用下面方法实现:
这里的getter和setter 可以对变量进行处理加工之后,向外界暴露,外界直接调用get/set的方法去读取更改_name这个属性
class Person{<!-- --> constructor(private _name:string){<!-- --> get name(){<!-- --> return this._name+' lee'; //可以将name加密之后暴露出去 } set name(name:string){<!-- --> //也可以保护变量 const realName=name.split('')[0]; //对得到的name 进行一个处理 this._name=realName } } const person=new Person('dell') console.log(person.name);//dell lee 不需要括号 person.name ='dell lee'
永远只生成一个实例,当发现 instance 已经创建了之后,就不再创建实例了
class Demo{<!-- --> private static instance:Demo private constructor(public name:string){<!-- -->}// 不允许被调用 规避创建实例的情况 static getInstance(){<!-- --> if(!this.instance){<!-- --> //如果没有创建 创建让instance储存 this.instance = new Demo('dell lee') } return this.instance; } } const demo1 =Demo.getInstance();
主要是把有共性的部分去做一个封装
抽象类只能被继承 不能被 new
抽象类的用法 extends 子类如果继承了父类的抽象类之后,必须在子类里面去实现父类的抽象方法
举个栗子:算长方形,圆形 椭圆的面积 直接写 class 那么每个 class 下面都需要写这个方法,这个方法可以被提取出来写在抽象类里,再长方形,圆形,椭圆都去继承这个抽象类,各个形状的面积计算公式是不一样的,具体实现在自己的类里面去写
具体写法:
abstract class Geom{<!-- --> //abstract 去定义一个抽象类 getType(){<!-- --> return 'gemo'; } abstract getArea():number //抽象方法 没有具体实现 } class Circle extends Geom{<!-- --> private r:number getArea(){<!-- --> return '123' } } class Square extends Geom{<!-- --> getArea(){<!-- --> return 'hello' } } class Triangle extends Geom{<!-- --> getArea(){<!-- --> return '' } }
修饰的工具
装饰器本身是一个函数
通过@符号来使用,不能直接使用装饰器,在标准里面属于实验性质的语法,需要在 tsconfig.json 文件中把配置项打开
function testDecorator(constructor:any){<!-- --> constructor.prototype.getName = () =>{<!-- --> console.log('dell') } console.log('decorator') } @testDecorator class Test{<!-- -->}//运行时机 会在类创建好之后,立马执行 const test = new Test();
收集的时候是从上到下依次收集
但是执行的时候会从下到上
function testDecorator1(constructor:any){<!-- --> console.log('decorator1') } function testDecorator2(constructor:any){<!-- --> console.log('decorator2') } @testDecorator1 @testDecorator2 class Test{<!-- -->} //控制台: //decorator2 //decorator1
有时候装饰器里面会存在一些判断,可以用工厂模式去包装装饰器,里面返回一个函数
function testDecorator(flag:boolean){<!-- --> //接收装饰器的参数 if (flag) {<!-- --> return function (constructor: any) {<!-- --> constructor.prototype.getName = () => {<!-- --> console.log('dell'); }; console.log('decorator'); }; }else{<!-- --> return function (constructor: any) {<!-- -->} } } @testDecorator(false)//装饰器返回的是一个函数,然后再调用该函数 class Test{<!-- -->}
方法装饰器接收三个参数
function getNameDecorator(target:any,key:string,descriptor:PropertyDecorator){<!-- --> console.log(target, key, descriptor); };
在创建完实例之后,对方法进行修改,如何让方法不被修改呢?如何让方法的原始值进行修改?
可以用 descriptor去控制
function getNameDecorator(target:any,key:string,descriptor:PropertyDescriptor){<!-- --> console.log(target, key, descriptor); descriptor.writable=false //方法不允许被修改 descriptor.value = function() {<!-- --> //让方法里面返回的原始值进行修改 return 'yeyeyeye' } }; class Test{<!-- --> name:string; constructor(name:string){<!-- --> this.name=name; } @getNameDecorator getName(){<!-- --> //class创建好之后,立刻对方法做一个装饰 return this.name } } const test =new Test ('dell') test.getName=()=>{<!-- --> //error return '123' }
访问器的装饰器接收的参数和方法装饰器是一样的
访问器装饰器接收三个参数
给set去写一个装饰器:
function visitDecorator( target: any, key: string, descriptor: PropertyDescriptor ) {<!-- --> console.log(target, key, descriptor); descriptor.writable = false; //方法不允许被修改 }; class Test{<!-- --> private _name:string; constructor(name:string){<!-- --> this._name=name; } get name() {<!-- --> return this._name; } @visitDecorator set name(name:string){<!-- --> this._name = name; } } const test =new Test ('dell') test.name = '123123' //走的是 set 方法 会报错 set 上面的装饰器写了descriptor.writable = false 方法不允许被修改 console.log(test.name) //走的是 get 方法
属性装饰器接收两个参数
可以自己定义descriptor 返回 新的descriptor 可以去覆盖原始 name 的descriptor
比如下面:name 是可以进行修改的,自己定义descriptor 将 writable 更改成 false 之后 将不能被修改了
function nameDecorator( target: any, key: string, ):any{<!-- --> console.log(target, key); const descriptor :PropertyDescriptor ={<!-- --> writable:false } return descriptor; }; class Test{<!-- --> name:string='dell'; } const test =new Test () test.name = '123123'
参数装饰器接收三个参数
function paramDecorator(target: any, method: string,paramIndex:number): any { console.log(target, method, paramIndex); //原型 getinfo 0 }; class Test{ getinfo(@paramDecorator name:string,age:number){ console.log(name,age) } } const test =new Test () test.getinfo('dell',30)
npm i -g @vue/cli //安装 vue 脚手架 vue create vueAddts //创建 vue 项目 vue add @vue/typescript //创建 @vue/typescript npm i vuex //安装 vuex
TS版: 需要先去导入两个组件 Component , Vue
Component组件注解,用来注册组件
vue-property-decorator 属性装饰器
如下:
<script lang="ts"> import {<!-- --> Component, Vue } from 'vue-property-decorator' import ResList from '@/components/ResList.vue' @Component({<!-- --> components: {<!-- --> ResList } }) export default class extends Vue {<!-- -->}
Vue版:对象的方式去描述整个vue
<script> export default {<!-- --> name: "App", data(){<!-- --> return{<!-- -->} } }; </script>
项目跟着TS+Vue.js视频写的一个标签应用,对新手非常友好,强烈推荐小伙伴去看~
功能:对标签添加删除分类修改
感兴趣的可以点击这项目链接查看
写项目的时候没有太关注这边,所以贴大佬链接,想看的小伙伴转场啦~ 了不起的 tsconfig.json 指南