一、TS基础
1.TypeScript的本质
- 是JS的超集,添加了注解的JavaScript,原有基础上
- ts是在开发阶段,生产都会转成js
- 是强类型语言吗 -- 不是; 强类型语言,数据类型不能隐式转换; ts可以,只是多了一层校验
2.基础类型写法
-
let num: number = 1;
JS原始类型:number,string,boolean,null,undefined,symbol
-
数组类型
let array1: number[] = [1,2,3]; //推荐写法 ,避免与JSX代码冲突,减少代码量 let array1: Array<number> = [1,2,3] //Array泛型 let array2: string[] = ['x','y'] -
元组类型Tuple
- 限制数组元素的个数和类型,适合实现多值返回
3.特殊类型
- any 可以绕过静态类型检测的一个作弊方法
可以把任何类型赋给any, 也可以把any赋给任何类型(never除外)
let anything:any = {}
//以下错均不会报错
anything.do()
anything = 1;
let num: number = anything
any类型的任意属性也是any
let z = anything.x.y.z //z类型也是any
z(); //不会报错
2. unknown 描述类型并不确定的变量
let result:unknow;
if(x){ result = x(); }
else if(y){result = y(); }
-
unknow相比any更安全,unknown只能赋值给unknown和any
-
但如果不缩小类型,对unknown执行操作会报错
let result: unknown; result.toFixed(); //提示ts(2571);
- void, 表示没有返回值的函数
- undefined,最大价值体现在接口类型上,表示可缺省,未定义的属性
- null主要体现在接口制定上,表示对象或属性可能是空值
- null/undefined也具备警示作用,容错处理
-
never 永远不会返回值的类型
抛错的函数 function ThrowError(msg: string): never{ throw Error(msg); }
never可以给所有类型赋值,但其他类型不能给never类型赋值
const props: {
id: number,
name?: never
}
这里实现了禁止写name属性,给name赋值就会报错
7.object: 表示非原始类型的类型(没什么用武之地)
declare function create(o: object | null):any;
create({});//可以
create(()=> null);//可以
create(2)// 报错
4.类型断言
TypeScript类型检测无法做到绝对智能
const num: number[] = [1,2,3,4];
const num2: number = num.find(item => item > 2);
// 报错 因为可能是数字,也可能是undefined
-
as语法做类型断言,相当于强制类型转换,结果一定是这个类型
const num2: number = num.find(item => item > 2) as number -
变量/值后面加 ! ,表示非空,排除null, undefined
const a :number | undefined = undefined; const b: number = a!;
- 使用的意义:告知编译器,运行时会有值
5.类型推断
类型标注后置,意味着编译器可以通过代码上下文推导类型
let x1 = 42; //推断出x1是number
let x2: number = x1; //ok
字面量类型
let str: 'string1' = 'string1';
let str2: string = 'string2';
str2 = str; // ok
str = str2 //报错
“马”比喻string类型,“黑马”比喻‘string1’类型;黑马肯定是马,但马不一定是黑马
-
字面量类型:限制函数的参数为指定的字面量类型集合
type Direction = 'UP'|'DOWN'; function move(dir: Direction){...} interface Config{ size:'small'|'big', margin:0|2|4, inEnable: true|false //和用boolean没有区别 }
字面量类型拓宽
通过let,var定义的变量/形参/属性,指定了初始值并且 没有显示添加类型注解; => 字面量类型拓宽:指定的字面量类型拓宽后的类型
let str = 'string'; //类型是string
let fun = (str='aaa') => str;
//(string) => string
字面量类型使用
function handle(url:string, method: 'GET' | 'POST'){}
const req = {
url:'http://...',
method: 'GET'
}
handle(req.url, req.method) //req.method会报错,req.method会被推断为string类型
const req = {
url:'http://...',
method: 'GET'
} as const;
类型缩小
-
类型守卫, 可以将函数参数的类型从any缩小到明确类型
fun(param : any){ if(typeof param === 'string'){ return ... } else if(typeof param === 'boolean'){ return ... } } 其他类型判断可以使用 in,instanceof -
处理联合类型场景
-
真值缩小 if(str)... ; !!str ; Boolean(str)
函数类型
type Adder = (a: number, b: number) => number; //ts函数类型定义
function print(fn: Adder){
fn(a,b);
}
const add: Adder(a,b)=>a+b;
函数返回值类型
-
可以没有显示return,此时函数返回值undefined;
-
但不能显示设置函数返回值为undefined类型,应该用void类型标识没有返回值的函数
-
可以不显示设置类型,函数会根据内容推断出返回类型
interface Enity{ add: (a: number, b number) => number } const obj:Enity = { add:(a,b) =>(a+b); }
函数参数类型
-
可选参数和默认参数
function log(x?:string){...} //参数可以不传 function log(x:string | undefined){...} //参数一定要传,类型必须是string 或 undefined -
剩余参数,ES6可以把多个变量收集到一个变量中
function sum(...nums: number[]){ return nums.reduce((a,b)=>a+b, 0) } sum(1, 2, 3); sum(1, '1'); //报错 -
参数值出现在上下文的时候,可以不定义类型
const name = [1,2,3]; name.forEach(function(item){ //item为name数组中的元素,可以推断出类型 console.log(item); })
Interface 接口
仅限于描述/约束对象,只有规范不实现
- 接口定义
interface language{
name: string;
age?: () => number; //?表示属性可缺省
// age: () => number | undefined不等价
[key: string]: any //可以自己增加额外的属性
}
使用可能缺省属性的时候 可以类型守卫 / Optional Chain
if(typeof XX.age === 'function'){
XX.age();
}
XX.age?();
-
定义函数类型(很少使用)
interface lan1{ (param: language): void } let study1: lan1 = param => {...} //内联类型 定义函数类型 type study = (param: language) => void //推荐写法 -
接口类型可以继承/被继承
接口类型的作用:将内联类型抽离出来,从而实现类型可复用
interface PayInterface{
handle(price:number): void{}
}
class Alipay implements PayInterface{
public handle(price:number): void{
console.log('zfbfk')
}
}
class Wepay implements PayInterface{
public handle(price:number): void{
console.log('wxfk')
}
}
Type 类型别名
type用来给类型起一个新名字,方便重复使用;可以是基本类型,对象,联合,交集
type username = string;
type userMsg = string | number;
type Person{
name: username,
user: userMsg
}
type lantype {
name: string;
age: () => number;
}
interface和Type区别
-
interface重复定义的接口类型,可以实现属性叠加,方便拓展;
-
type重复定义类型别名会报错,
interface Animal{ name: string } interface Bear extends Animal{ honey: boolean } const bear: Bear = { name: 'aaa' honey:true } ------------------ type Animal = { name: string } type Bear = Animal & { honey: boolean }
联合和交叉类型
-
联合类型 |,数据类型不唯一的情况
function fun(size: string | number, unit: 'px'|'rem'){} 也可以用类型别名抽离,再联合 type unit1 = 'vh'|'vw'; type unit2 = 'px'|'rem'; unit1|unit2 -
交叉类型 &,多个类型合并成一个
type useless = string & number; // never 无用
- 用途:合并接口类型(求并集) type inter = {id: number} & {age: number}
枚举类型
-
定义
enum Day{ Sunday, Monday }
-
JS中没有与枚举类型对应的 原始实现,会把枚举类型转译为,属性为常量,值从0递增的对象
//使用 work(Day.Sunday); work(0); -
数字枚举 仅指定常量命名,定义的就是从0开始的数字集合
enum Day{ Sunday = 1, //指定初始值 Monday } -
枚举类型-字符串枚举
enum Day{ Sunday = 'Sunday', Monday = 'Mnday' } -
枚举类型-异构枚举(鸡肋) 支持枚举类型同时拥有数字,字符类型
泛型
-
定义:类型 是一个参数,将原来具体的类型进行参数化,即 把参数param的类型定义为一个参数,而不是一个明确的类型,等函数调用时再传入明确的类型
function reflect<P>(param: P){ // <P>定义一个泛型P,指定param参数类型为P return param; } const str1 = reflect<string>('string'); //const str1 = reflect('string'); //参数有传值,类型可省 const str2 = reflect<number>(1); -
目的:约束类型成员之间的关系(函数参数和返回值,类/接口成员和方法之间的关系)
-
泛型约束
function rel<P extends number | string | boolean>(param: P):P{ ...//限制函数传参指定的几个类型 }
限制传入的参数,必须有length属性 -- 需要继承实现
function longer<Type extends { length: number}>(a: Type, b: Type){
if(a.length > b.length){ return a; }
else{ return b; }
}
也可以
Type extends string | any[]
3. 泛型和类结合
class Collection<T>{
data: T[] = [];
public push(...items: T[]){ this.data.push(...items)}
public shift(): T{ return this.data.shift() }
}
type User = { name: string; age:number }
const user1 = { name: 'aaa', age: 23 }
const coll = new Collection<User>()
collections.push(user1)
4. 接口使用泛型
interface Artical<B,C>{
title: string
isLock: B
comments: C[]
}
type Comment = {
content:string
author:string
}
const aa: Artical<Boolean, Comment> = {
title:'ts网站',
isLock: true,
comments: [{content:'评论', author:'作者'}]
}
装饰器 -decorator
- 配置ts装饰器环境
"experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
"emitDecoratorMetadata": true,
类装饰器
想使用一个类,并且增加一个方法,但又不影响这个类本身,就可以在自己要使用的地方,加一个装饰器
function add(target: Function): void{
target.prototype.startClass = function(){
...增加一个函数
}
}
@add
class Course{
constructor(){
...
}
}
装饰器语法糖,@add 相当于 add(Course)
例:可以用装饰器给不同的类统一添加方法/属性
const MessageDecorator: ClassDecorator = (target: Function) => {
target.prototype.message = (content:string) =>{
console.log(content);
}
}
@MessageDecorator
class LoginControl{
public login(){
this.message('登陆成功')
}
}
@MessageDecorator
class Music{}
new LoginControl().login()
方法装饰器
const showDec: MethodDecorator= (...args: any[])=>{
// args[0].name = 'aaaa'
console.log(args)
[{ name: 'aaaa' }, args[0] 为函数的原型对象 //如果是静态方法,args[0]为构造函数
'show', args[1] 函数名称
{
value: [Function: show],
writable: true,
enumerable: false,
configurable: true
}
]
}
class User{
@showDec
public show() {}
}
例:装饰器工厂,可以在使用装饰器的时候传参数
// 装饰器
const showDec: MethodDecorator= (...args: any[])=>{
const method = args[2].value;
args[2].value = ()=>{
setTimeout(()=>{
method()
},2000)
}
}
//装饰器工厂
const SleepDecorator = (times: number): MethodDecorator =>{
return (...args: any[])=>{
const method = args[2].value;
args[2].value = ()=>{
setTimeout(()=>{
method()
},times)
}
}
例:ts装饰器全局异常管理
const ErrorDec: MethodDecorator = (target:object,propertyKey: string,descriptor:PropertyDescriptor) => {
const method = descriptor.value;
descriptor.value = () => {
try {
method()
}catch(error:any){
console.log(error.message)
}
}
}
class User{
@ErrorDec
find(){
throw New Error('asd');
}
}
原理解析
-
源码输入
let a: number = 2; -
scanner扫描 => 识别内容生成数据流
{ 'let': 'keyword', 'a': 'identifier', '=': 'assignment', '2': 'integer', ';': 'eos' (end of statement) }//没有number //number 是用来做检测的 -
解析器 parser => 生成语法树AST
-
绑定器 创建symbols 上面的AST对应的每个节点,node.symbols
-
校验器 checker,检查TS语法错误
-
发射器 emitter根据每个结点的检查结果,翻译成js