TypeScript 详解
一、TS基础概念
1. 什么是TS?
a. 对比原理 TS vs JS
- 他是Javascript的一个超集,在原有的语法基础上,添加了可选的静态类型和基于类的面向对象编程。
面向项目: TS:面向于解决大型复杂项目中,架构以及代码维护的复杂场景 JS: 脚本化语言,用于面向单一简单场景页面
自主检测(语法): TS:编译期间(预编译 TS 转换成 JS),TS 编译器主动发现并纠正错误 JS: 运行时报错,无编译
类型检测: TS: 弱类型,支持对于动态和静态类型的检测 JS: 弱类型,无静态类型选项
运行流程 TS: 依赖编译,依赖 TS 工程化体系 JS: 直接在浏览器中运行
复杂特性 TS - 模块化、泛型、接口
b. 安装运行
npm install -g typescript
tsc -v
tsc test.ts
// .ts => .js => 浏览器执行环境
// 面试点:所有类型检测和语法检测 => 编译时
2. TS基础类型与写法
- boolean、string、number、array、null、undefined
// es
let isEnabled = true;
let class = 'zhaowa';
let classNum = 2;
let u = undefined;
let n = null;
let classArr = ['basic', 'execute'];
// TS
let isEnabled: boolean = true;
let class: string = 'zhaowa';
let classNum: number = 2;
let u: undefined = undefined;
let n: null = null;
let classArr: string[] = ['basic', 'execute'];
let classArr: Array<string> = ['basic', 'execute'];
- tuple - 元组(补充写法)
let tupleType: [string, boolean];
tupleType = ['zhaowa', true];
- enum - 枚举(新增写法)哈希表实现
// 数字类枚举 - 默认从零开始,从上往下依次递增
enum Score {
BAD,
NG,
GOOD,
PERFECT
}
let sco: Score = Score.BAD;//0
// 字符串类型枚举
enum Score {
BAD = 'BAD',
NG = 'NG',
GOOD = 'GOOD',
PERFECT = 'PERFECT',
}
// 反向映射
enum Score {
BAD,
NG,
GOOD,
PERFECT
}
let scoName = Score[0] // BAD
let scoVal = Score['BAD']; // 0
// 异构
enum Enum {
A, // 0
B, // 1
C = 'C',
D = 'D',
E = 6,
F, // 7
}
// 面试题:指出异构的枚举值
// 面试题: 手写将其转化为JS实现
let Enum;
(function(Enum) {
// 正向
Enum['A'] = 0;
Enum['B'] = 1;
Enum['C'] = 'C';
Enum['D'] = 'D';
Enum['E'] = 6;
Enum['F'] = 7;
// 反向
Enum[0] = 'A';
Enum[1] = 'B';
Enum[6] = 'E';
Enum[7] = 'F';
})(Enum || (Enum = {}));
- any、unknown、void、never
// any - 绕过所有类型检查 => 类型检测和编译筛查都全部失效 any会导致不确定性的蔓延
let anyValue: any = 123;
anyValue = 'anyValue';
anyValue = false;
let value1:boolean = anyValue;//anyValue = false;可以通过value1的检查
// unknown - 绕过了赋值检查 => 禁止更改传递 unknown限制传参不会蔓延
let unknownValue: unknown;
unknownValue = true;
unknownValue = 123;
unknownValue = 'unknownValue';
let value1: unknown = unknownValue; // OK
let value1: any = unknownValue; // OK
let value1: boolean = unknownValue; // NOK
// void - 声明函数的返回值 没有返回值
function voidFunction(): void {
console.log('void function');
}
// never - 函数永不返回
function error(msg: string): never {
throw new Error(msg);
}
function longlongLoop(): never {
while(true) {}
}
- object / {} - 对象
// TS将js的object分成两个接口来定义
interface ObjectConstructor {
create(o: object | null): any;
}
const proto = {};
//非原始类
Object.create(proto);
Object.create(null);
Object.create(undefined); // Error
// Object 原始类
// Object.prototype上属性
interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueof(): Object;
hasOwnProperty(v: PropertyKey): boolean;
isPrototypeOf(v: Object): boolean;
}
// {} - 空对象定义空属性
const obj = {};
obj.prop = 'zhaowa'; // Error
// 空对象是可以使用Object上的所有方法的
obj.toString(); // OK
二、接口 - interface
- 对行为的一种抽象,具体行为由类实现 对对象的补充说明
interface Class {
name: string;
time: number;
}
let zhaowa: Class = {
name: 'ts',
time: 2
}
// 只读 & 任意
interface Class {
readonly name: string;
time: number;
}
// 面试题 - 只读和JS的引用是不同的 < = > 和const区分,const地址不可变不可重新赋值,对地址里内容的变化无法感知 => 执行阶段
let arr: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = arr;
ro[0] = 12;
ro.push(5);
ro.length = 100;
arr = ro;
// ERROR 任何更改都会报错
// 任意
interface Class {
readonly name: string;
time: number;
[propName: string]: any;//多个任意
}
const c1 = {name: 'JS', time: 1}
const c2 = {name: 'browser', time: 1}
const c3 = {name: 'ts', level: 1, time: }
三、交叉类型 - &
// 合并
interface A {x: D}
interface B {x: E}
interface C {x: F}
//逐层解释
interface D {d: boolean}
interface E {e: string}
interface F {f: number}
//合并
type ABC = A & B & C;
let abc: ABC = {
x: {
d: false,
e: 'zhaowa',
f: 5
}
}
// 合并冲突 将不会存在
interface A {
c: string,
d: string
}
interface B {
c: number,
e: string
}
type AB = A & B;
let ab: AB = {
d: 'class',
e: 'class'
}
// => 且关系 => c: never
四、断言 - 类型声明、转换(开发者和编译器的提前告知交流)
- 编译状态在产生作用
// 尖括号形式声明
let anyValue: any = 'hi zhaowa';
let anyLength: number = (<string>anyValue).length;
// as声明
let anyValue: any = 'hi zhaowa';
let anyLength: number = (anyValue as string).length;
// 非空! - 只判断不为空
type ClassTime = () => number;
const start = (classTime: ClassTime | undefined) => {
let num = classTime!(); // 具体类型待定,但是非空确认
}
// 使用场景 通用数据处理逻辑
const tsClass: number | undefined = undefined;
const zhaowa: number = tsClass!;//保证不为空
// 底层实现后,上层应用1 不合适
const tsClass = undefined;
const zhaowa = tsClass;
// => 产出undefined可能
// 肯定化保证
let score: number;
startClass();
console.log(2 * score);
function startClass() {
score = 5;
}
let score!: number; // 告知编辑器,运行时会被赋值的
五、类型守卫 - 保障语法规定的范围内,额外的确认
- 多态 - 多重状态类型
interface Teacher {
name: string;
courses: string[];
score: number;
}
interface Student {
name: string;
startTime: Date;
score: string;
}
type Class = Teacher | Student;
// in守卫 - 是否包含某种属性
function startCourse(cls: Class) {
if ('courses' in cls) {
// 老师
}
if ('startTime' in cls) {
// 学生
}
}
// typeof / instanceof - 类型分类场景下的身份确认
function startCourse(cls: Class) {
if (typeof cls.score === 'number') {
// 老师
}
if (typeof cls.score === 'string') {
// 学生
}
}
function startCourse(cls: Class) {
if (cls instanceof Teacher) {
// 老师
}
if (cls instanceof Student) {
// 学生
}
}
// 自定义类型
const isTeacher = function(cls: Teacher | Student): cls is Teacher {
// 老师……
}
const getInfo = (cls: Teacher | Student) => {
if (isTeacher(cls)) {
return cls.courses;
}
}
六、TS进阶方案
1. 函数重载
class Class {
start(name: number, score: number): number;
start(name: string, score: string): string;
start(name: string, score: number): number;
//类型守卫 =》函数重载
start(name: Comnbinable, score: Comnbinable) {
if (typeof name === 'number' || typeof score === 'number') {
// 处理
}
if (typeof name === 'string' || typeof score === 'string') {
// 处理
}
if (typeof name === 'string' || typeof score === 'number') {
// 处理
}
}
}
2. 泛型 - 重用
function startClass <T, U>(name: T, score: U): T {
// 逻辑
}
function startClass <T, U>(name: T, score: U): string {
// 逻辑
}
function startClass<T,U>(name:T,score:U):T{
return (name + String(score)) as any as T;
}
function startClass <T, U>(name: T, score: T): T {
return (name + score) as T;
}
3. 装饰器 - decorator
function Zhaowa(target: Function): void {
target.prototype.startClass = function(): void {
// 逻辑
}
}
@Zhaowa //类装饰器,给类批量增加公用的属性和方法
class Course {
constructor() {
// 业务逻辑
}
//startClass()
}
// 属性/方法装饰器
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
{
operation: '=',
left: {
keyword: 'var',
right: 'a'
}
right: '2'
}
// 4. binder 绑定器
// AST节点 node.symbol <=> 辅助校验器
// 5.1. 校验器 checker
// ts节点语法检查 => 类型检查
// 5.2 发射器 emitter
// 翻译完成每个node节点的内容,翻译成js => 输出