1.const regexp:RegExp = /^1/ 正则类型
2.const date:Date = new Date() 日期类型
3.const error:Error = new Error()
new 出来的返回的都是对象所以前面必须是大写
如
let b:Boolean = new Boolean(1)
unknown和any区别
1.unknown比any更安全
2.unknwon类型不能去调用属性和方法不能点出来东西
3.unknwon只能充当父类型使用 不能赋值给别的类型 他只能赋值给自身或any类型
Object 属于原型链顶端所以包含所有类型可以赋值所有类型的值
object不可以赋值原始类型其他引用类型都可以赋值 数组 对象 函数都可以
{}也是包含所有类型但是无法对变量进行任何赋值操作 少用
interface接口 一般用于定义对象类型
如果遇到重名的内部属性会自动重合 属性类型不能多也不能少
使用索引签名方法 定义任意key 就是前面的会校验规则后面的值不在校验
interface Axxx { name: string age:number readonly cb:() =>boolean [propName:string]:any //值的类型定义什么那么后面就得是什么类型 } let a:Axxx = { name:'小猫', age: 18, a:1, b:2 } //interface定义函数类型 interface Fn { (name:string):number[] } const fn:Fn = function (name:string) { return [1] }
1.?可选 2.不允许在外面修改的话使用修饰符readonly使他变为只读属性 一般多用于函数
3. interface接口继承extends 继承后属性相加在一块
定义数组类型
number[] or Array<number> 两种方法都可以
// 定义对象数组使用 interface X { name: string } const arr:X[] = [{name:'qwes'}] //定义多维数组 let arr:number[][] = [[1]] or let arr:Array<Array<number>> // 如果数组中什么类型都有 用any或者元组类型就是一个一个的定义类型 let arr:any[] = [1,'qwe',{}] // 剩余参数可以是具体类型或者任何类型的数组 function a(...args:any[]) { let a:IArguments = arguments } // arguments内部类型 interface A { callee: Function length:number [index:number]:any }
函数类型
参数写几个就得传几个 除非给他默认值 或者 加?可选运算符 如 num?:number
// interface定义参数 interface User { name:string age:number } const fn = function(user:User):User { return user } let a = fn({name:'',age:18})
函数重载 重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。如果参数类型不同,则操作函数参数类型应设置为any。参数数量不同你可以将不同的参数设置为可选。
// 重载函数 两套规则 function fn(params:number):void function fn(params:number,params2:string):void // 用于执行逻辑的 function fn(params:any,params2?:string):any { return params + params2 }
联合类型
let phone:number | string = let fn = function(type:number | boolean):boolean { return !!type }
交叉类型
interface People { name:string age:number } interface Man { sex:number } // 两个接口的类型都需要添加 const qwe = (man:People & Man):void { console.log(man) }
类型断言
let fn = function (num:number | string):void { console.log((num as string).length) } fn(123456) // interface A { run:string } interface B{ build:string } // 就想在形参上点出run这个时候将形参就断言为类型A 但是值为undefined let fn = function (type:A | B):void { console.log((tyoe as A).run) } fn({build:'123'}) // 可以在window上使用 (window as any).abc = 123 //欺骗ts const fn = (type:any):boolean=> { return type as boolean } let bbb = fn(1)
class类
class类的修饰符
public内部外部都能访问
private 私有变量只能在内部访问
protected 内部和子类中访问
// ts中声明变量需要在前面提前声明一遍 如果某个变量没有使用ts报红的话需要给个默认值或者赋个值 class Person { public name:string private age:number protected sub:boolean = false // 静态变量 static aaa:string = '1232131' // 构造器中也不能通过this访问静态变量或方法可以通过class直接调用Person.run() constructor (name:string,age:number,sub:boolean) { this.name = name this.age = age this.sub = sub } // 静态方法 静态方法中不能使用this的变量只能使用static定义的变量 static run() { reutrn '789' } } // 在子类中也不能访问到private定义的变量 但是public和protected可以访问 class Man extends Person { constructor () { super('tang', 22 ,false) } } // 静态属性可以通过类直接调用 Person.aaa Person.run() // 创建实例 let p = new Person('小满',22,false)
通过接口约束class类函数
interface Person { run(type:boolean):boolean } interface H { set():void } class A { params:string constrctor(params) { this.params = params } } // 多个类型用,链接 class Man extends A implements Person,H { run (type:boolean):boolean { return type } }
class抽象类
应用场景如果你写的类实例化之后毫无用处此时我可以把它定义为抽象类 或者 你也可以把它作为一个基类:通过创建一个派生类继承抽象类去实现它的一些方法,这个抽象类也能叫基类
// 抽象类 无法创建实例 abstract class A { name:string constructor(name:string) { this.name = name } // 如果不用abstract定义函数B类继承后还是可以正常使用的 // 加了抽象修饰符就无法在函数中写逻辑了 abstract getName(): string } // 派生类 class B extends A { constructor() { super('qwe') } // 按照抽象类定义 getName():string { return this.name } }
元组类型
元组是固定数量不同类型的元素的组合
// 元组类型 加上readonly为只读不可以更改 const arr: readonly [number, boolean] = [1, false] arr[0] = 555 //错误 arr.push(1) //错误 //加上名称可以设为可选 const arr: readonly [x:number,y?:string] = [1,'qwe'] //type 类型别名 type first = typeof arr[0]
枚举类型
数字枚举、字符串枚举、异构枚举
const枚举编译成js时会直接将枚举编译为值而普通枚举会编译为对象
// 默认值为0,1,2 如果给第一个赋值1后面会累加 如过要给其中一个赋值字符串那么就要全部赋值 enum Color { red green blue } // 调用 Color.red // 接口枚举 interface A { red:Color.red } let obj:A = { red:Color.red } // const 枚举 const enum Types { success, fail } let code:number = 0 if(code === Types.success) {} // 反向映射 数字类型可以映射 字符串不可以 enum Types { success = 456 } let success:number = Types.success // 打印出来他对应的key console.log(Types[success])
类型推论
声明了一个变量但是没有定义类型 ts会在没有明确的指定类型的时候推测出一个类型,这就是类型推论
类型别名
type可以定义任何类型
type和interface区别
1. interface可以通过extends继承
2.type可以写联合类型 interface必须在内部定义属性后才能定义
3. interface可以重名合并 type不可以
// extends 在type中是包含的意思 左边的值会作为右边类型的子类型 type num = 1 extends number ? 1 : 0 //返回1
类型层级
1.any unknow
2.Object
3.Number
4.number string
5.never
never
表示不应该存在的状态
type aaa = string | number //这个时候aaa就是never 不存在这种情况 // 应用场景 //不可能有返回值 只抛出异常 function error(message:string):never { throw new Error(message) } // 死循环也可以使用 function loop():never { while(true) { } } //如果后面的人增加了一个接口但是函数中没有添加逻辑这个时候default兜底逻辑就会报错提醒 因为never类型无法被赋值 interface A { type:'保安' } interface B { type:'小说' } interface C { type:'摸鱼' } type All = A | B | C function type(val:All) { switch(val.type) { case '保安': break case '小说': break case '保安': break default: const check:never = val; } }
Symbol
es6新增类型 通过Symbol构造函数创建的 ,可以传递参数为唯一标识,只支持string和number类型的参数。
一般用再对象属性类名当键名比较多
let s:symbol = Symbol('二蛋') let num:symbol = Symbol('二蛋') console.log(s === num) //不等于 因为内存地址指针位置不同所以是唯一值 let obj = { [num]: 'value', [s]: '12', name: 'tang', age: 18 } // 这些方法都无法循环到symbol属性的键名 for(const k in obj) { console.log(k) } console.log(Object.keys(obj)) console.log(Object.getOwnPropertyNames(obj)) console.log(JSON.stringify(obj)) // 以下方法可以循环到 // 可以取到对象里symbol类型的键名 但取不到普通类型的 console.log(Object.getOwnPropertySymbols(obj)) // 普通类型和symbol类型都可以取到 console.log(Reflect.ownKeys(obj))
Symbol.iterator迭代器和生成器for of
数组或者伪数组都有这个属性但是{}对象是没有的
// 通过迭代器方式获取值 当到最后一个没有值时dong为true let arr:Array<number> = [4,5,6] let it:Iterator<number> = arr[Symbol.iterator]() console.log(it.next()) // {value: 4, done:false} console.log(it.next())// {value: 5, done:false} console.log(it.next())// {value: 6, done:false} console.log(it.next())// {undefined, done:true} // 封装一个小的迭代器函数 迭代器原理 type mapKeys = string | number let set:Set = new Set([1,2,3]) let map:Map<mapKeys, mapKeys> = new Map() map.set({1,'wang'}) map.set({2,'yang'}) function gen(erg:any) { let it:Iterator<any> = erg[Symbol.iterator]{} let next:any = {done:false} while(!next.done) { next = it.next() if(!next.done) { console.log(next) } } } gen()
for of就是js用迭代器实现的语法糖直接返回值但是也不支持{}对象
ts泛型
函数泛型:语法为函数名字后面跟一个<参数名>参数名可以随便写 例如写一个T当我们使用这个函数的时候把参数的类型传进去就可以了
function add<T>(a:T,b:T):Array<T> { return [a,b] } add<number>(1,2) function sub<T | U>(a:T,b:U):Array<T | U> { reutrn [a,b] } sub<number, string>(1,'a') // 泛型约束 interface Len { length:number } function getLength<T extends Len>(val:T) { return val.length }
使用keyof约束对象
其中使用了TS泛型和泛型约束。首先定义了T类型并使用extends关键字继承object类型的子类型然后使用keyof操作符获取T类型的所有键,它的返回类型是联合类型,最后利用extends关键字约束K类型必须为keyof T联合类型的子类型
// 约束key function prop<T,K extends keyof T>(obj:T,key: K) { return obj[key] } let o = {a:1,b:2,c:3} prop(o, 'a') //类中用泛型 class Sub<T>{ attr:T[] = [] add(a: T): T[] { return [a] } } let s = new Sub<number>() s.attr([1,2,3]) s.add(3)
type interface中使用泛型
// type type A<T> = number | string | T let a:A<boolean> = false // inteface interface Data { mas:string } let obj:Data = { mas:'qwe' }
泛型可以一次定义多个也可以给默认值
function add<T = number,K = string>(a:T,b:K):Array<T | K> { return [a,b] }
tsconfig.json 配置文件
常用
1.include
指定编译文件默认是编译当前目录下所有的ts文件2.exclude
指定排除的文件3.target
指定编译js 的版本例如es5 es64.allowJS
是否允许编译js文件5.removeComments
是否在编译过程中删除文件中的注释6.rootDir
编译文件的目录7.outDir
输出的目录8.sourceMap
代码源文件9.strict
严格模式10.module
默认common.js 可选es6模式 amd umd 等
namespace命名空间
ts和ECMA2015一样,任何包含顶级import或export的文件都被当成一个模块,相反的,如果一个文件不带有顶级的import或export声明,那么它的内容被视为全局可见的
命名空间通过export将想要暴露的部分导出,如果不用export导出是无法读取其值的
// 正常使用 namespace A { export const a = 1 } console.log(A.a) // 嵌套命名空间的使用 namespace A { export namespace C { export const c = 5 } } console.log(A.C.c) // 简化命名空间 import AAA = A.C console.log(AAA.c) // 抽离命名空间就是导入一个命名空间然后使用 // 命名空间如果重名就会合并
三斜线指令
三斜线指令是包含单个XML标签的单行注释。注释的内容会做为编译器指令使用
三斜线指令仅可放在包含它的文件的最顶端。一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。
///<reference path="...”/>指令是三斜线指令中最常见的一种。它用于声明文件间的 依赖.三斜线引用告诉编译器在编译过程中要引入的额外的文件
你也可以把它理解能import,它可以告诉编译器在编译过程中要引入的额外的文件/// <reference path="index2.ts" /> //使用方法和import一样引入后可以直接使用 /// <reference types="node" /> //可以引入声明文件
声明文件d.ts
declare当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能
有两种方法 一种是通过 npm i --save-dev @types/express(这块是你需要声明的插件)
另一种是创建.d.ts自己声明
import express from 'express' const app = express() const router = express.Router() app.use('/api', router) router.get('/api', (req: any, res: any) => { res.json({ code: 200 }) }) app.listen(9001, () => { console.log('9001') }) // 声明文件 declare module 'express' { interface Express { (): App Router(): Router } interface Router { get(path: string, cb: (req: any, res: any) => void) } interface App { use(paht: string, router: any): void listen(port: number, cb?: () => void) } const express: Express export default express }
Mixins
ts混入Mixins其实vue也有 可以把它看作合并
1.对象混入
可以使用es6的Object assign合并多个对象
interface Name { Name: string } interface Age { age: number } interface Sex { sex: string } let name:Name = {name: 'qwe'} let age:Age = {age:18} let sex:Sex = {sex:1} Object.assign(name,age,sex)
2.类混入
class A { type!: boolean changeType(): void { this.type = !this.type } } class B { name!: string getName(): string { return this.name } } class C implements A, B { type: boolean = false name: string = 'qwe' changeType!: () => void getName!: () => string } mixins(C, [A, B]) function mixins(curCls: any, itemCls: any[]) { itemCls.forEach(item => { Object.getOwnPropertyNames(item.prototype).forEach(name => { curCls.prototype[name] = item.prototype[name] }) }) } let qwe = new C() console.log(qwe.getName())
Decorator 装饰器是一项实验性特性,在未来的版本中可能会发生改变
它们不仅增加了代码的可读性,清晰地表达了意图,而且提供一种方便的手段,增加或修改类的功能
若要启用实验性的装饰器特性,你必须在命令行或
tsconfig.json
里启用编译器选项
装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。
//定义一个类装饰器函数 他会把ClassA的构造函数传入你的watcher函数当做第一个参数 const watcher: ClassDecorator = (target: Function) => { target.prototype.getParams = <T>(params: T):T => { return params } } //使用的时候 直接通过@函数名使用 @watcher class A { constructor() { } } const a = new A(); console.log((a as any).getParams('123'))
装饰器工厂
其实也就是一个高阶函数 外层的函数接受值 里层的函数最终接受类的构造函数
const watcher = (name: string): ClassDecorator => { return (target: Function) => { target.prototype.getParams = <T>(params: T): T => { return params } target.prototype.getOptions = (): string => { return name } } } @watcher('name') class A { constructor() { } } const a = new A(); console.log((a as any).getParams('123'));
装饰器组合
就是可以使用多个装饰器
const watcher = (name: string): ClassDecorator => { return (target: Function) => { target.prototype.getParams = <T>(params: T): T => { return params } target.prototype.getOptions = (): string => { return name } } } const watcher2 = (name: string): ClassDecorator => { return (target: Function) => { target.prototype.getNames = ():string => { return name } } } @watcher2('name2') @watcher('name') class A { constructor() { } } const a = new A(); console.log((a as any).getOptions()); console.log((a as any).getNames());
方法装饰器
返回三个参数
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
成员的名字。
成员的属性描述符。
const met:MethodDecorator = (...args) => { console.log(args); } class A { constructor() { } @met getName ():string { return '小满' } } const a = new A();
属性装饰器
返回两个参数
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
属性的名字。
const met:PropertyDecorator = (...args) => { console.log(args); } class A { @met name:string constructor() { } } const a = new A();
参数装饰器
返回三个参数
对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
成员的名字。
[ {}, 'setParasm', 0]
const met:ParameterDecorator = (...args) => { console.log(args); } class A { constructor() { } setParasm (@met name:string = '213') { } } const a = new A();