TS(上)

文章目录

  • 一、TS的基础概念
  • [二、接口 - interface](#二、接口 - interface)
  • [三、交叉类型 - &](#三、交叉类型 - &)
      • [3.1 合并](#3.1 合并)
      • [3.2 合并冲突](#3.2 合并冲突)
  • 四、断言
      • [4.1 尖括号形式](#4.1 尖括号形式)
      • [4.2 as声明](#4.2 as声明)
      • [4.3 非空判断 - 只确定不是空](#4.3 非空判断 - 只确定不是空)
      • [4.4 断言的最高境界: 肯定断言](#4.4 断言的最高境界: 肯定断言)
  • 五、类型守卫
  • 六、TS进阶
      • [6.1 函数重载](#6.1 函数重载)
      • [6.2 泛型](#6.2 泛型)
      • [6.3 装饰器](#6.3 装饰器)

一、TS的基础概念

1.什么是TS

它是JS的一个超集,在原有的语法基础上,添加强类型并切换为基于类的面向对象语言。

TS属于强类型语言,JS属于弱类型语言,强类型语言支持静态语言和动态语言,弱类型语言支持动态语言。

TypeScript JavaScript
类型检测 强类型语言 ,支持动态语言静态语言 弱类型语言,无静态类型选项
面向项目 面向解决大型的复杂项目、架构、代码维护复杂场景 脚本语言, 脚本化语言,用于面向简单的页面场景
自主检测 可以在编译期间自主发现并纠正错误 运行时,执行报错
运行流程 先被浏览器编译成js语言 在浏览器可以直接使用
复杂性 支持模块化、接口、泛型 -

强类型语言和弱类型语言

强类型语言 :强类型语言不允许改变变量的数据类型,除非进行强制类型转换。
弱类型语言:定义与强类型语言相反,一个变量可以被赋予不同数据类型的值。PHP、Ruby、Python。

动态语言和静态语言

静态语言 :编译时确定变量的数据类型,运行期间不可以改变其结构,比如C++、Java、C#。
动态语言:运行时才确定数据结构和类型,变量使用之前不需要类型声明,比如:JavaScript、PHP、Ruby、Python。

2.TS基础类型和语法

2.1 boolean、string、number、array、null、undefined

ts 复制代码
// es
let isEnable = false;
let className = 'zhaowa';
let classNum = 2;
let u = undefined;
let n = null;
let classArr = ['basic', 'execute'];

// TS 
let isEnable: boolean = false;
let className: 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'];

不能更改它们的变量类型,否则会直接报错。错误示范:

ts 复制代码
let myName: String = "zhangsan";
myName = true;//Error:Type 'true' is not assignable to type 'String'.

2.2 tuple - 元组

元组:每一个元素有独立的类型。允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。

ts 复制代码
let tupleType: [string, boolean] = ['basic', false];

2.3 枚举

枚举主要分为数字类型枚举、字符串类型枚举、异构。
语法 :enum + 变量名,意思是定义了一个叫做"变量名"的枚举类型
优点:以为一组数值赋予友好的名字。

  1. 数字枚举
    含义 :枚举成员都是number类型,如果没有对枚举对象中的枚举成员赋值,那么会默认从枚举对象中的第一个枚举成员的值是0,并依次递增。
    特点
  2. 反应映射
  3. 枚举成员的值可以不用初始化
ts 复制代码
// 数字型枚举 - 默认从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',
}

// 反向映射
enum Score {
   BAD,  // 0
   NG,   // 1
   GOOD,  // 2
   PERFECT, // 3
}

let scoreName = Score[0]; // BAD
let scoreVale = Score["BAD"]; // 0

// 异构
enum Enum {
   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 = {}))

2.4 any、unknow、void、nerver

ts 复制代码
// any - 绕过所有类型检查 => 类型检测和编译筛查取消
let anyValue: any = 123;

anyValue = "anyValue";
anyValue = false;
let value1: boolean = anyValue;

// unknown -绕过赋值检查 => 禁止更改传递
let unknownValue: unknown;

unknownValue = true;
unknownValue = 123;
unknownValue = "unknownValue";

let value1: unknown = unknownValue; // OK
let value2: any = unknownValue;  // OK
let value3: boolean = unknownValue; // NOK

// void - 声明返回为空
function voidFunction(): void {
    console.log('void');
}

// never - 永不能执行完 or 永远error
function errorGen(msg: string): never {
    throw new Error(msg);
}

function infiniteLoop(): never {
    while(true) {
        // 业务逻辑
    }
}

2.5 any和unknown区别

any类型:表示任意类型,即可以赋值给任何类型的变量。使用any类型后,该变量可以进行任何操作而不会触发类型检查,相当于关闭了类型检查器的所有限制。
unknown类型:表示未知类型,即不确定具体类型的变量。与any不同,使用unknown类型后,该变量在没有进行类型检查或类型断言之前,不能被赋值给其他变量或进行任何操作。

使用方式:

  1. any类型的使用:在需要灵活处理类型的场景下,可以使用any类型。例如,当从第三方库中获取数据时,由于其类型无法确定,可以将其声明为any类型以避免类型错误。
  2. unknown类型的使用:当我们需要对类型进行安全的操作时,可以使用unknown类型。通常会配合类型判断(type guard)或类型断言来使用。类型判断可以帮助我们在确定类型之后,对变量进行类型转换或操作。
  3. 注意:使用any类型会绕过类型检查器的所有限制,容易引入类型错误和调试困难。为了保持代码的类型安全性,建议尽可能避免使用any类型,而是使用更精确的类型或通过类型断言将unknown类型转换为其他确定类型。

3.对象的三种状态:object/Object/{}

object - 非原始类型

用来描述后天创建类型的类型描述

js 复制代码
interface ObjectConstructor {
    create(o: object | null): any;
}

const proto = {};

Object.create(proto);
Object.create(null);
Object.create(undefined); // Error

Object - 原型上的属性,一个集合

js 复制代码
// Object.prototype 上的属性
interface Object {
    constructor: Function;
    toString(): string;
    toLocaleString(): string;
    valueOf(): Object;
}

// 定义了Object类属性
interface ObjectConstructor {
    new(value: any): Object;
    readonly prototype: Object;
}

{} - 定义空属性对象

js 复制代码
// {} - 定义空属性对象
const obj = {};

obj.prop = "props"; // NOK
obj.toString(); // OK

二、接口 - interface

什么是接口?对行为模块的抽象,具体的行为是有类来实现

js 复制代码
// 描述对象内容
interface Class {
    name: string;
    time: number;
}

let zhaowa: Class = {
    name: 'typescript',
    time: 2
}

// 只读
// readonly 的声明不可改,它是一个只读属性
interface Class {
    readonly name: string;
    time: number;
}

// 面试题 - JS的引用类型操作不同 
// 经常和const对比,const 创建的引用类型是可以改变的,readonly类型的声明是不可被改变的(让一个对象或者数组真正只读)
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接口的可描述程度
// 任意可添加属性
interface Class {
    readonly name: string;
    time: number;
    [propName: string]: any;
}

const c1 = { name: "JS" };
const c2 = { name: "browser", time: 1 };
const c3 = { name: "ts", level: 1 };

三、交叉类型 - &

3.1 合并

把一个复杂的东西拆成较简单的小块去做独立的抽象说明。

优点:更加的灵活。

ts 复制代码
// 合并
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
    }
};

3.2 合并冲突

ts 复制代码
interface A {
    c: string;
    d: string;
}
interface B {
    c: number;
    e: string;
}

// 声明一个类型,是A和B的合并
type AB = A & B;

let ab: AB;
c - never (永远达不到)

合并的关系是 "且",如果两个类型不一致 则相交的结果是never。所以c的类型既是string又是number,不存在这样的类型。

四、断言

类型的声明和转换(和编译器做了一个告知交流)

4.1 尖括号形式

语法:<type>+value,尖括号中填写具体的类型。

ts 复制代码
//anyValue是any类型,在使用时候将其断言为string类型
let anyValue: any = 'hi zhaowa';
let anyLength: number = (<string>anyValue).length;

<string>:执行到这里的时候阶段性的类型是字符串,虽然声明的时候是any类型,但是执行到这个阶段的时候是string

4.2 as声明

语法:value as type,as后跟具体类型。

ts 复制代码
let anyValue: any = 'hi zhaowa';
let anyLength: number = (anyValue as string).length;

4.3 非空判断 - 只确定不是空

(不能保证执行到这里是字符串,但是能保证执行到这里不是空)

含义:非空断言用!表示,它用来断定某变量一定不是 null 和 undefined。

ts 复制代码
type ClassTime = () => number;

const start = (ClassTime: ClassTime | undefined) {
    // 业务逻辑
    // if (额外判断逻辑) {
        let time = classTime!(); // 具体类型待定,但是非空确认
    // }
}

// 面试题 在ts里可以这样去写吗,非空处理成功了吗
const tsClass: number | undefined = undefined;
const zhaowa: number = tsClass!;
console.log(zhaowa);
// 不行,因为这里使用断言无效。
// 在严格模式下会转义成
const tsClass = undefined;
const zhaowa = tsClass;
console.log(zhaowa); // undefined
//不建议在赋值的时候断言,赋值的时候可以使用逻辑功能做非空限制

4.4 断言的最高境界: 肯定断言

肯定化保证赋值

ts 复制代码
let score: number;
startClass();
console.log('' + score); // 使用前赋值

function startClass() {
    score = 5;
}

// let score!: number; - 提前告知

五、类型守卫

语法规范范围内,额外的确认

多态 - 多种状态(单个变量有多种类型)

  1. in 定义属性场景下内容的确认
    通过类型守卫实现了额外的规范化的流,不能交叉类型合并可以通过导流
ts 复制代码
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);
    }
}
  1. typeof / instanceof - 类型分类场景下的身份确认
    通过类型判断:string进来是学生,(返回为good,perfect),number进来是老师,(返回为 1-5分)
ts 复制代码
// typeof 判断
function class(name: string, score: string | number) {
    if (typeof score === "number") {
        return "teacher:" + name + ":" + score;
    }
    if (typeof score === "string") {
        return "student:" + name + ":" + score;
    }
}
// instanceof 判断
const getName = (cls: Class) => {
    if(cls instanceof Teacher) {
        return cls.courses;
    }
    if(cls instanceof Student) {
        return cls.startTime;
    }
}
  1. 自定义类型
ts 复制代码
// cls是teacher或student中的一种,当前这个函数是用来判断它是不是老师
const isTeacher = function (cls: Teacher | Student): cls is Teacher {
    return 'courses' in cls; 
}

const getName = (cls: Teacher | Student) => {
    if(isTeacher(cls)) {
        return cls.courses;
    }
}
// 类型别名 & 联合类型

六、TS进阶

6.1 函数重载

ts 复制代码
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: Combinable, score: Combinable) {
            if (typeof name === 'string' || typeof score === 'string') {
                return 'student:' + name + ':' + score; 
            }
        }
    }
// Combinable:多种类型的重载
// 对实际数据的类型做匹配
    const course = new Course();
    course.start('yunyin', 5);

6.2 泛型

让模块可以支持多种类型数据 - 让类型声明和值一样,可以被赋值和传递

ts 复制代码
  function startClass <T, U>(name: T, score: U) : T {
        return name + score;
    }

    console.log(startClass<String, Number>("yunyin", 5));

    // T、U、K - 键值、V - 值、E - 节点、元素

6.3 装饰器

装饰器:一个设计模式的概念,不局限于语言,在任何的实例上去嵌套一个通用的能力。

一个壳:给函数、 类、 类的属性或方法,套了一个通用的壳。

ts 复制代码
// 配置:tsc --target ES5 --experimentalDecorators
    // "experimentalDecorators": true

    // 类装饰器
    function Zhaowa(target: Function): void {
        target.prototype.startClass = function(): void {
            // 通用功能
        }
    }

    // 属性装饰器
    function propsWrapper(target: Object, key: string) {
        // 属性的统一操作
        Object.defineProperty(target, key, {
        })
    }

    // 方法装饰器 - target: Object, propertyKey: string, descriptor: TypePropertyDescript

    @Zhaowa
    class Course {
        constructor() {
            // 业务逻辑
        }

        @propsWrapper
        public name: string;

        @methodDec
        
    }
相关推荐
L耀早睡2 分钟前
mapreduce打包运行
大数据·前端·spark·mapreduce
HouGISer16 分钟前
副业小程序YUERGS,从开发到变现
前端·小程序
outstanding木槿22 分钟前
react中安装依赖时的问题 【集合】
前端·javascript·react.js·node.js
霸王蟹1 小时前
React中useState中更新是同步的还是异步的?
前端·javascript·笔记·学习·react.js·前端框架
霸王蟹1 小时前
React Hooks 必须在组件最顶层调用的原因解析
前端·javascript·笔记·学习·react.js
专注VB编程开发20年1 小时前
asp.net IHttpHandler 对分块传输编码的支持,IIs web服务器后端技术
服务器·前端·asp.net
爱分享的程序员2 小时前
全栈项目搭建指南:Nuxt.js + Node.js + MongoDB
前端
隐含3 小时前
webpack打包,把png,jpg等文件按照在src目录结构下的存储方式打包出来。解决同一命名的图片资源在打包之后,重复命名的图片就剩下一个图片了。
前端·webpack·node.js
lightYouUp3 小时前
windows系统中下载好node无法使用npm
前端·npm·node.js
Dontla3 小时前
npm cross-env工具包介绍(跨平台环境变量设置工具)
前端·npm·node.js