目录
typeof instanceof - 类型分类场景下的确认
TS 基础概念
1. TypeScript是JavaScript的一个超集,是在原有语法基础上添加了可选的静态类型和基于类的面向对象编程。
与JavaScript的区别
1)面向项目:
TS:面向于解决大型复杂项目中,架构以及代码维护复杂场景,利于未来的维护
JS:脚本化语言,用于面向单一简单场景
2)自主检测:
TS:编译期间就能主动发现并纠正错误
JS:运行时报错,逐行执行,有问题报错
3)类型检测:
TS:支持对于动态和静态类型的检测,但依旧是弱类型,会转换为js给浏览器读取运行
JS:弱类型,无静态类型选项
4)运行流程:
TS:依赖编译,依赖工程化体系
JS:直接在浏览器中运行
5)复杂特性:
TS:模块化,泛型,接口
通过npm安装TypeScript
编译时会转换成js再给浏览器执行,若单独创建ts文件进行编译(tsc + 文件名)会生成同名js文件
// .ts => .js => 浏览器执行环境
所有类型检测和语法检测在ts编译时报错,剩下的执行报错还是在js时报错
TS基础类型与写法
boolean string number array null undefined
// 声明变量
let isEnable: boolean = true
let str: string = 'hello'
let num: number = 2
let strArr: string[] = ['a', 'b']
let strArr2: Array<string> = ['a', 'b']
数组类型不统一怎么办
1. tuple -元组
let turpleType: [string, boolean]
turpleType = ['a', true]
2. enum 枚举
1). 数组类枚举 - 默认从0开始,从上往下依次递增
enum Score {
BAD,
NG,
GOOD,
PERFECT
}
let sco: Score = Score.BAD // 0
console.log(sco)
2). 字符串类型枚举
enum Score1 {
BAD = 'BAD',
NG = 'NG',
GOOD = 'GOOD',
PERFECT = 'PERFECT'
}
let scoName = Score[0] // BAD 可以取到对应值 正向反向皆可
let scoVal = Score['BAD'] // 0
3). 异构
enum Enum {
A,
B,
C = 'C',
D = 'D',
E = 1,
F = 8,
G,
}
问题:指出异构的枚举值, 并手写转换为js
let enumVal = Enum['E'] // 1
let enumVal1 = Enum[1] // E, 覆盖了原本B的位置
let enumVal2 = Enum['B'] // 1
let enumVal3 = Enum['G'] // 9 从上一个数字继续递增
手写代码参考编译后的js文件
any unknown void
1. any -
绕过所有类型检查,所有的类型检测和编译筛查全部失效
// 缺点:会导致不确定性,既可以声明 也可以传值,不建议频繁使用
let anyVal: any = 12; // 所以后续赋值可以将anyVal修改为任意类型
anyVal = 'a'
anyVal = false
let val:boolean = anyVal // 可以继续赋值给别人
2. unknown -
绕过了赋值检查,禁止更改传值
let unknownVal: unknown;
let unknownVal: unknown;
unknownVal = true
unknownVal = 123
unknownVal = 'b'
// 赋值给别人时 只能赋值给unknown/any类型的变量
let val1: unknown = unknownVal // ok
let val2: any = unknownVal // ok
// let val3: boolean = unknownVal // no
3. void -
声明函数的返回值
function voidFunction():void {
console.log('void function')
}
// never - 函数永不返回
// 用于抛出错误
function errorFunc(msg: string): never {
throw new Error(msg)
}
// 维持状态的循环函数
function longLoop(): never {
while(true) {
// do sth 但是不建议,会造成性能上的损耗,占据内存无法释放
}
}
object {} - 对象
// TS将js的object分成两个接口来定义
interface
// 可以对object进行属性扩展
interface ObjectConstructor {
create(o: object | null): any; // 可以传入一个对象或者null
}
const proto = {}
Object.create(proto)
Object.create(null) // 或传入一个对象,不可传入undefined 会报错
// 接口定义 属性之间 逗号,分号或者不写都是可以的
Object.prototype
Object.prototype上的属性 默认object都会有的属性,无需定义也可以使用
interface Object {
constructor: Function;
toString(): string;
tolocaleString(): string;
valueof(): Object;
hasOwnProperty(v: PropertyKey): boolean;
isPrototypeOf(v: Object): boolean;
}
{} 空对象定义空属性
const obj = {}
// obj.prop = 'abc' // 不可以 会报错,未声明prop属性
obj.toString(); // ok 可继承Object上的所有方法
interface 是对行为的一种抽象,具体行为由类实现,定义一套规则,后面用到的时候按规则解析校验
// 接口可以只读/任意
interface PersonClass {
name: string;
age: number;
readonly gender: string;
[propName: string]: any; // any可以使不确定的参数等进行接收,比如后端接口返回数据
}
let Zhang: PersonClass = {
name: 'zhang',
age: 18,
gender: 'male'
}
console.log(Zhang)
// Zhang.gender = 'female' // error 不可更改
// 问题:只读和js的const有什么区别
// 一个引用类型的变量,const指向一个存储地址,只要引用位置不变即可,内部值可以改变
// readonly不可以对数组、对象里的任意属性改变,也不可重新赋值
// 与执行阶段和判断方式有关,ts编译时判断,根据语法结构来判断
交叉类型
// 合并
interface A {x: D}
interface B {x: E}
interface C {x: F}
interface D {d: boolean}
interface E {e: string}
interface F {f: number}
interface G {
f: string;
g: boolean
}
type ABC = A & B & C
let abc: ABC = {
x: {
d: false,
e: 'aaa',
f: 6,
}
}
// 合并冲突 且关系 f: 不会出现
type FG = F & G
// let fg: FG = {
// g: false
// }
断言 -
类型声明、转换 开发者和编译器的预先告知和交流
// 编译状态在产生作用
// <>尖括号形式声明
let anyValue: any = 'hello world'
let anyLength: number = (<string>anyValue).length
// as声明
let anyLength1: number = (anyValue as string).length
非空判断-
只判断不为空 使用场景:通用中台数据处理逻辑
type ClassTime = () => number
const startTime = (classTime: ClassTime | undefined) => {
let num = classTime!(); // ! 表示具体类型待定,但是非空是确定的
console.log(num)
}
const num1: number | undefined = undefined
const num2: number = num1!
类型守卫
保障语法规定的范围内。额外的确认
多态 - 多重状态类型
in 守卫 判断是否包含某种属性
key in data 判断data中是否包含key这个属性
interface A = {}
interface B = {}
typeof instanceof - 类型分类场景下的确认
typeof data.key === 'number' 判断data的key属性是否为number类型
data instanceof A 类型确认 data是否为A
TS 进阶方案
1. 函数重载
// Combinable联合类型需要定义数据类型的范围,就可以定义类型为Combinable
type Combinable = string | number
function run(num:number): void;
function run(str: string, flag: boolean): void;
function run(str: string, flag?: boolean): void;
function run(str: Combinable, flag?: boolean): void{
if(typeof str === 'string'){
console.log("fur")
}else{
console.log(1)
}
}
run(1)
run("fur")
run("fur",true);
2. 泛型
// 适合复用,可以将类型也作为参数传递判断
function getInfo <T, U>(name: T, age: U): T {
// do sth
return name
}
function getInfo1 <T, U>(name: T, age: U): string { // 返回值确认是string类型
// do sth
return String(age)
}
function getInfo2 <T, U>(name: T, age: U): T {
return (name + String(age)) as any as T
}
建议不要把所有参数都用泛型来传递,尽量将确认的信息放进来,以免后期维护成本过高
装饰器 - decorator
加上装饰器后 类就具有了装饰器的功能,给类批量增加一些属性和方法,可以包裹一部分内容供多处复用
function func(target: Function): void {
target.prototype.sayhi = function(): void {
// do sth
console.log('hi')
}
}
@func
class ClassDemo {
constructor() {
// do sth
}
}
let demo = new ClassDemo
console.log(demo)
// 属性/方法装饰器
function nameWrapper(target: any,key:string):void {
Object.defineProperty(target, key, {})
}
class Course {
constructor() {// 业务逻辑
}
@nameWrapper
public name: string;
}
TS原理流程
1、源码
var a = 2;
2. scanner扫描器生成令牌流
[
'var': 'keyword',
'a': 'identifier',
'=': 'assignment',
'2': 'imteger',
';': 'eos' // 结束符
]
3. parser 解析器 生成AST
{
opration: '=',
left: {
keyword: 'var',
right: 'a'
},
right: '2'
}
4、binder绑定器
AST节点 node.symbol <=> 辅助校验器
5、校验器和发射器
校验器 checker
ts节点语法检查 <=> 类型检查
发射器 emitter
翻译完成每个node节点的内容,翻译成js => 输出