TypeScript 详解
一、TS的基础概念
1. 什么是TS(与JS的区别)
-
对比原理:他是JS的一个超集,在原有的语法基础上,添加强类型并切换为基于类的面向对象语言
-
面向项目:
- TS - 面向解决大型的复杂项目、架构、代码维护复杂场景
- JS - 脚本化语言,用于面向简单页面场景
-
自主检测
- TS - 编译时,主动发现并纠正错误
- JS - 运行时,执行报错
-
类型检测
- TS - 强类型语言,支持动态和静态的类型检测
- JS - 弱类型语言,无静态类型选项
-
运行流程
- TS - 依赖编译,依靠编译打包实现在浏览器段的运行
- JS - 可直接在浏览器端运行
-
复杂特性
- TS - 模块化、接口、泛型
2. TS的基础类型和语法
- boolean、string、number、array、null、undefined
//es
let isEnable = false;
let clasas = 'yiban';
let classNum = 2;
let u = undefined;
let n = null;
let arr = [1,2];
//ts
let isEnable: boolean = false;
let class: string = 'yiban';
let classNum: number = 2;
let u:undefined = undefined;
let n: null = null;
// 统一方式 & <>
let arr:number[] = [1,2];
let arr:Array<number> = [1,2];
tuple - 元祖
let tupleType: [string, boolean] = ['a', false]
enum - 枚举
// 数字型枚举 - 默认从0开始,依次递增
enum Score {
BAD, //0
NG, //1
GOOD, //2
PERFECT //3
}
let score: Score = Score.BAD
// 字符串类型枚举
enum Score {
BAD = 'BAD',
NG = 'NG,
GOOD = 'GOOD',
PERFECT = 'PERFECT,
}
// 反向映射
let scoreName = Score[0]; //BAD
let scoreVale = Score['Bad']; // 0
// 异构 (没定义就是默认数字型枚举)
enum Eunm{
A, // 0
B, // 1
C = 'C',
D = 'D',
E = 8,
F, // 9
}
// 面试题:异构类型每一项的枚举值 => js本质实现(手写一个异构枚举的实现)
let Enum;
(function(Enum){
// 正向
Enum['A'] = 0;
Enum['B'] = 1;
Enum['C'] = 'C';
Enum['D'] = 'D';
Enum['E'] = 8;
Enum['F'] = 9;
// 逆向
Enum['0'] = 'A';
Enum['1'] = 'B';
Enum['8'] = 'E';
Enum['9'] = 'F' ;
})(Enum || (Enum = {}))
any、unknow、void
any - 绕过所有类型检查 => 类型检测和编译筛查取消
let value1: any = 123;
value1 = 'a';
value1 = false;
let value2: boolean = value1;
// 以上均不会报错
unknown - 绕过赋值检查 => 禁止更改传递
let unknownValue : unknow;
unknownValue = true;
unknownValue = 123;
unknownValue = '123';
let value1: unknown = unknownValue;
let value2: any = unknownValue;
//以上均不会报错
let value3: boolean = unknownValue; //报错
void - 声明返回为空
function voidFun(): void{
console.log('1');
}
never - 永不执行 or 永远error
function errorGen(msg: string): never{
throw new Error(msg)
}
function infiniteLoop(): never{
while(true){
//业务代码
}
}
object / Object/ {} - 对象
object - 非原始类型 后天创建类型的类型描述/限制
interface ObjectConstruction {
create(o: object | null): any;
}
const proto = {};
Object.create(proto);
Object.create(null);
Object - 定义了Object类属性
// 保留Object.prototype上的属性
interface Object {
constructor: Function;
toString(): string;
toLocaleString(): string;
valueOf(): Object;
...
}
interface ObjectConstructor{
new(value: any): Object;
readonly prototype: Object;
}
{} - 定义空属性对象(想要不报错用下面的interface定义对象)
const obj = {};
obj.prop = 'props'; //Error
obj.toString(); // ok
二、接口 - interface
对行为模块的抽象,具体的行为是有类来实现
// 描述对象内容
interface People {
name: string,
age: number,
}
let zhangsan: People = {
name: 'zhangsan',
age: 14
}
//只读
interface People {
readonly name: string,
age: number,
}
// 面试题 (可以延伸为 如何确保const对象不被改写,用ts的只读)
let arr: number[] = [1,2,3,4];
let ro: ReadonlyArray<number> = arr;
ro[0] = 12; //赋值 - Error
ro.push(5); // 增加 - Error
ro.length = 10; // 长度改写 - Error
arr = ro; // 覆盖 - Error
// 任意可添加属性
interface People {
readonly name: string,
age: number,
[propName: string]: any; // 用来添加任意属性的
}
const c1 = {name: 'lisi'};
const c2 = {name: 'wangwu', time: 16};
const c3 = {name: 'bingliu', age: 20};
三、交叉类型 - &
合并
interface A{
inner: D;
}
interface B{
inner: E;
}
interface C{
inner: F;
}
interface D{
d: boolean;
}
interface E{
e: string;
}
interface F{
f: number;
}
type ABC = A & B & C;
let abc: ABC = {
inner: {
d: false,
e: 'className',
f: 5,
}
}
合并冲突
interface A{
c:string;
d:string;
}
interface B{
c:number;
d:string;
}
type AB = A & B;
let ab: AB;
// 合并的关系是‘且’ => c只能是never
四、 断言 - 类型的声明和转换(和编译器做了一个告知交流)
编译时作用
// 尖括号形式
let anyValue: any = 'h1';
let anyLength: number = (<string>anyValue).lenght;
// as 声明
let anyValue: any = 'h1';
let anyLength: number = (anyValue as string).lenght;
// 非空判断 - 只确定不是空
type ClassTime = () => number;
const start = (ClassTime: ClassTime | undefined) {
...
let time = ClassTime!; // 具体类型待定 但是非空确认
}
// 面试题
const tsClass: number | undefined = undefined;
const ts: number = tsClass!;
console.log(ts);
// 转义成
const tsClas s= undefined;
const ts = tsClass!;
console.log(ts); // undefined
// 肯定断言 - 肯定化保证赋值
let score: number;
startClass();
console.log('' + score);
function startClass(){
score = 5;
}
// let score!: number; -- 提前告知肯定化赋值
五、类型守卫 - 语法规范范围内,额外的确认
多态 - 多种状态(多种类型)
in - 定义属性场景下内容的确认
interface Teacher(){
name: string;
courses: string[];
}
interface Student(){
name: string;
startTime: Date
}
type Class = Teacher | Student;
function startCourse(cls: Class){
if('courses' in cls){
console.log('Courses:' + cls.courses)
}
if('startTime' in cls){
console.log('startTime:' + cls.startTime)
}
}
typeof / instanceif - 类型分类场景下的身份确认
function class(name: string, score: string | number){
if(typeof score === 'nubmer'){
return 'teacher'
}
if(typeof score === 'string'){
return 'student'
}
}
const getName = (cla: Class){
if(cls instanceof Teacher){
return 'teacher'
}
if(cls instanceof Student){
return 'student'
}
}
六、 TS进阶
1. 函数重载 - 复用(对实际数据的类型做不同的匹配)
class Course{
start(name:number, score: number): number;
start(name:string, score: string): string;
start(name:string, score: number): string;
start(name:number, score: string): string;
start(name: Conbinable, score: Conbinable){
if (typeof name === 'string' || typeof score === 'number'){
return 'children'
}
}
}
const course = new Course();
course.start('zhangsan', 5);
2. 泛型 - 复用(让模块可以支持多种类型数据 - 让类型声明和值一样, 可以被赋值和传递)
function startClass <T, U>(name: T, score: U) : T {
return name + score;
}
console.log(startClass<Number, String>('zhangsan', 5));
// T、U、K - 键值类、V - 值、 E - 节点、元素
3. 装饰器 - decorator
需安装
// 类装饰器
function fun(target: Function): void{
target.prototype.startStude = function(): void{
// 通用功能
}
}
// 属性装饰器
function propsWrapper(target: any, key: string){
//属性的统一操作
Object.defineProperty(target, key, {
...
})
}
// 方法装饰器 - target:Object, propertyKey: string, descriptor: TypePropertyDescript
@fun // 类装饰器
class Course {
constructor(){
//包含了startStude方法
}
@propsWrapper // 属性装饰器
public name: string;
@methodDec // 方法装饰器
}