TypeScript核心类型系统完全指南

第一部分:TypeScript基础入门

TypeScript简介

1.什么是TypeScript
  • TSJS 的超集,简单来说就是为 js添加了类型限定。众所周知js的类型系统存在 先天的缺陷,程序中很多的问题都是因为错误的 类型导致的。

    ts属于静态类型编程语言,js属于动态编程语言

2. Ts的优势
  • ts是前端项目的首选语言,ts中存在类型推断机制 不需要在代码中的每个地方都显示标注

体验TS与配置

体验ts
  1. ts交于js会对数据类型进行检查

  2. 只需要第一次 定义变量的时候对 数据类型进行注解

    ts 复制代码
     let age:number=18;
    //:number 是类型注解  表示age变量的类型是number
常见的TS类型注解
  • 原始类型

    • number
    • string
    • boolean
    • symbol
    • null
    • undefined
  • 对象类型

  • object(数组 对象 函数)

  • 联合类型

    • 自定义类型(类型别名)
    • 接口
    • 元组
    • 字面量类型
    • 枚举
    • void
    • any
配置tsconfig.json
  • 需要在根目录中被指ts的配置文件,这是ts开发必备的 操作之一

    json 复制代码
    {
      "compilerOptions": {
        "target": "ES2020",
        "module": "CommonJS",
        "strict": true, 
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      }
    }

第二部分:TypeScript核心类型系统

基础类型

原始类型

原始类型就是常见的 字符串 数字 布尔值 未定义 等等类型 基本上js怎么使用那么ts就这么使用 只是需要添加一些类型注解而已

ts 复制代码
let age: number=18;
let name: string='张三';
let sex: boolean=true;
let symbol: symbol=Symbol('123');
let nullValue: null=null;
let undefinedValue: undefined=undefined;
//js 里面怎么用 ts就是怎么用
数组类型

数组类型属于对象类型,在对象类型中每个子类型都有自己的细分语法

数组类型的类型注解 : number[](推荐使用) 或者 :Array<string> 如果数组中存在多种数据类型就使用联合类型 (number | string)[]

ts 复制代码
//推荐写法
let numbers: number[]=[1,2,3,4,5];
let objArr:Object[]=[{},{}]
//其他写法
let strings:Array<string>=['1','2','3'];
//数组中含有多种类型数据 --> 联合类型
let arr: (number | string)[]=[1,'2',3,'4',5];
元组类型

元组可以看作确定元素个数与类型的数组,在部分场景里面会 使用到确定元素个数的数组类型 这种类型就叫做元组类型(比如:地图的经纬度)

元组类型类型注解 ::[number,number]

ts 复制代码
let Position=[31.232,12.653];
//元组的类型也不一定必须要一样
let Position2:[number,string]=[31.232,'12.653'];

枚举类型

枚举类型可以作为字面量类型的平替方案 枚举类型类似于字面量类型+联合类型的组合形态

枚举的值称为命名常量

定义枚举:

通过enum关键词定义 使用{}包裹命名常量

ts 复制代码
enum Direction{up,down,right,left}

枚举类型使用:

当使用枚举类型的函数需要调用的时候 只能通过枚举命名常量的属性来作为函数的参数

ts 复制代码
// 方向枚举
enum Direction {
    Up,
    Down,
    Left,
    Right
}

// 状态枚举
enum Status {
    Pending = "PENDING",
    Approved = "APPROVED",
    Rejected = "REJECTED"
}

// 函数参数使用枚举
function move(direction: Direction) {
    switch (direction) {
        case Direction.Up:
            console.log("向上移动");
            break;
        case Direction.Down:
            console.log("向下移动");
            break;
        // ...其他情况
    }
}

// 调用函数时使用枚举
move(Direction.Up);
枚举的种类

1.数字枚举

数字枚举是最常见的枚举类型,默认情况下第一个成员的值为0,后续成员按顺序递增:

ts 复制代码
enum Direction {
    Up,     // 值为 0
    Down,   // 值为 1
    Left,   // 值为 2
    Right   // 值为 3
}

// 使用枚举
let dir: Direction = Direction.Up;  // 值为 0
console.log(dir);  // 输出: 0

可以手动设置枚举成员的值:

ts 复制代码
// 设置起始值
enum Direction {
    Up = 1,    // 值为 1
    Down,      // 值为 2
    Left,      // 值为 3
    Right      // 值为 4
}

// 为每个成员设置具体值
enum Direction {
    Up = 1,
    Down = 3,
    Left = 5,
    Right = 9
}

2.字符枚举:

字符串枚举的每个成员都必须显式地初始化为字符串字面量,它们没有自增长行为:

ts 复制代码
enum Direction {    Up = "UP",    Down = "DOWN",    Left = "LEFT",    Right = "RIGHT" }
// 使用字符串枚举 
let dir: Direction = Direction.Up;
console.log(dir); // 输出: "UP"
  1. 异构枚举

虽然不建议使用,但技术上是可以混合字符串和数字成员的:

typescript

ts 复制代码
enum BooleanLikeHeterogeneousEnum {    No = 0,    Yes = "YES", }

字面量类型

字面量类型是一种特殊的类型,它将变量的类型限制为特定的值。与普通的stringnumberboolean类型不同,字面量类型不仅指定了值的类型,还指定了值的具体内容。

字面量类型的特点:

  1. 精确性:字面量类型比普通类型更加精确,限定了变量只能是特定的值
  2. 常量推断 :使用const声明的变量,TypeScript会自动推断为字面量类型
  3. 可组合性 :通过联合类型(Union Types)可以组合多个字面量类型

字面量类型用法:

ts 复制代码
let str1='hello ts' //string类型
const str2='hello ts' //hello ts 字面量类型
  • 字面量类型通常与联合类型结合使用,用于限制函数参数或配置对象的取值范围

    ts 复制代码
    // 限制函数参数的取值
    function setPosition(direction: 'left' | 'right' | 'up' | 'down') {
        // 函数体
    }
    
    // 调用时只能传入指定的字符串字面量
    setPosition('left');  // 正确
    setPosition('forward');  // 错误
    
    // 配置对象的属性限制
    interface Config {
        theme: 'light' | 'dark';
        size: 'small' | 'medium' | 'large';
    }
    
    const config: Config = {
        theme: 'dark',   // 只能是'light'或'dark'
        size: 'medium'   // 只能是'small'、'medium'或'large'
    };

与枚举类型相比

通常字面量类型与枚举类型可以替换

ts 复制代码
// 使用字面量类型
type Direction = 'up' | 'down' | 'left' | 'right';

// 使用枚举类型
enum DirectionEnum {
    Up,
    Down,
    Left,
    Right
}

联合类型

联合类型(Union Types)是TypeScript中的一种高级类型特性,它允许一个变量或参数可以是多种类型中的一种。联合类型使用竖线(|)分隔每个类型,表示"或"的关系。 后续在高级类型特性里面会详细介绍联合类型的各种使用方法

基本语法:

ts 复制代码
// 基本语法:Type1 | Type2 | Type3
let value: number | string;
value = 123;     // 正确,number类型
value = "hello"; // 正确,string类型
value = true;    // 错误,boolean类型不在联合类型中

复杂类型

对象类型

对象类型就是在描述对象的结构与各个属性的类型与 方法类似

1.基本写法
ts 复制代码
let person:
{
    name:string;
    age:number;
    sayHi(name:string,age:number):void
} = {
    name:'张三',
    age:18,
    sayHi(name:string):void{
        console.log('hi',name)
    }
}
2.箭头函数写法 :
ts 复制代码
//箭头函数写法
let person2:{
    name:string;
    age:number;
    // 箭头的后面写返回值类型 
    sayHi:(name:string)=>void
} = {
    name:'张三',
    age:18,
    sayHi:(name:string):void=>{
        console.log('hi',name)
    }
}
3.对象类型的可选属性
ts 复制代码
//对象类型可选属性
let person3:{
    name:string;
    age:number;
    // 箭头的后面写返回值类型 
    sayHi:(name:string)=>void;
    sex?:string;
} = {
    name:'张三',
    age:18,
    sayHi:(name:string):void=>{
        console.log('hi',name)
    },
    //sex:'man' //可有可无
}
函数类型

函数类型就是在js的基础上单独为行数的 参数 与返回值类型进行类型标注.

单独标注参数类型与返回值类型

就是单对为函数的参数与返回值进行类型标注

ts 复制代码
function add(X:number,Y:number):number{
    return X+Y;
}
同时标注二者类型

这是第一种写法 可读性很差 前面两个类型定义是定义两个参数的 后面一个类型定义是定义返回值类型的

ts 复制代码
const add2:(num1:number,num2:number)=>number=(num1,num2)=>{
    return num1+num2;
}

下面是可读性更高的一中 写法

ts 复制代码
// 定义函数类型别名
type AddFunction = (num1:number, num2:number) => number;

// 使用类型别名
const add2:AddFunction = (num1, num2) => {
    return num1 + num2;
}

箭头函数常用的定义方法

ts 复制代码
const add1 = (X:number, Y:number):number => {
    return X + Y;
}
返回值为void
ts 复制代码
function add3(X:number,Y:number):void{
    console.log(X+Y);
}
可选参数类型

在参数后加一个? 就是可选参数 但是不建议使用可选参数在需要计算的函数中. 值得注意的是 必选参数一定要放在可选参数的前面

ts 复制代码
function add4(X:number,Y?:number):void{
    console.log(X+(Y??1));
}

//必选参数不能位于可选参数之后
function mySlice(start?:number,end?:number):void{
    console.log('开始',start,'结束',end);
}
mySlice()

特殊类型

any类型

any类型是我们极不推荐使用的类型 因为any类型不会对代码进行保护 和js基本没有两样了. 如果每个变量都说用any类型,那代码就和js基本一模一样了,失去了ts作为静态类型语言的作用了

ts 复制代码
let a:any = 123;
a = '123';
//any类型会忽略类型检查 还不如不用ts
void类型

void类型表示没有任何类型,通常用于函数没有返回值的情况。如果变量被注解为void类型一般只能赋值为null或者是undefined

基本用法
ts 复制代码
// 函数没有返回值时,返回类型标记为void
function sayHello(): void {
    console.log("Hello!");
    // 不需要return语句,或者可以return;
}

// 等同于
function sayHello2(): void {
    console.log("Hello!");
    return; // 可以显式返回undefined
}

// 变量声明为void类型(不常用)
let unusable: void = undefined; // void类型只能赋值为undefined或null
实际应用场景
ts 复制代码
// 事件处理函数通常没有返回值
function handleClick(event: Event): void {
    console.log("按钮被点击了");
}

// 日志记录函数
function logMessage(message: string): void {
    console.log(`[LOG]: ${message}`);
}
null和undefined

在TypeScript中,null和undefined都有各自的类型,分别是null和undefined类型。

基本用法
ts 复制代码
// null类型
let nullValue: null = null;

// undefined类型
let undefinedValue: undefined = undefined;

// 在严格模式下,null和undefined只能赋值给any类型和它们各自类型
let num: number = null; // 错误:在严格模式下不允许
let str: string = undefined; // 错误:在严格模式下不允许
与联合类型结合使用
ts 复制代码
// 变量可以是字符串或null
let userName: string | null = null;
userName = "张三"; // 正确

// 变量可以是数字或undefined
let userAge: number | undefined = undefined;
userAge = 25; // 正确

// 函数返回值可能是对象或null
function findUser(id: number): User | null {
    // 查找用户逻辑
    // 如果找到返回User对象,否则返回null
    return null;
}
在React中的应用
ts 复制代码
// React中常见的状态初始化为null
const [user, setUser] = useState<User | null>(null);

// 使用可选链操作符安全访问属性
console.log(user?.name); // 如果user为null,不会报错
never类型

never类型表示永远不会发生的值的类型。它是TypeScript类型系统中的底部类型

使用场景
ts 复制代码
// 1. 函数抛出异常,永远不会有返回值
function throwError(message: string): never {
    throw new Error(message);
}

// 2. 函数中有无限循环,永远不会结束
function infiniteLoop(): never {
    while (true) {
        // 无限循环
    }
}

// 3. 类型守卫中的never
function exhaustiveCheck(value: never): never {
    throw new Error(`Unexpected value: ${value}`);
}
类型特点
  1. 底部类型neverTypeScript类型系统中的底部类型,它是所有类型的子类型
  2. 不可赋值 :除了never本身,没有其他值可以赋值给never类型
  3. 类型推断 :在某些情况下,TypeScript会自动推断出never类型

第三部分:类型高级特性

类型别名(Type Alias)

类型别名 即为自定义类型 当统一类型被多次使用时,可以通过类型别名 简化该类型的使用

定义与使用

使用type关键字来创建类型别名

ts 复制代码
// 使用type关节子创建类型别名
type myType=(number|string)[]
let arr1:myType=[1,2,'3'];
console.log(arr1);

接口(Interface)

一般情况下如果一个对象类型被多次使用的时候,为了达到复用的目的,会使用接口来描述对象的类型

定义:
ts 复制代码
 interface Person{
    name:string;
     age:number;
    sayHi():void;
}
 let person1:Person={
     name:'张三',
     age:18,
     sayHi(){
         console.log('hi',this.name)
     }
 }
接口的继承

接口可以使用extends来继承另一个接口中的类型注解

ts 复制代码
//如果两个接口有公共属性 就可以通过继承的方式实现复用
interface People{
    name:string;
}

interface Teacther extends People{
    age:number;
    subject:string;
}

let t: Teacther={
    name:'张三',
    age:18,
    subject:'Math'
}
接口的合并
ts 复制代码
// 接口声明合并
interface Window {
    title: string;
}

interface Window {
    ts: TypeScriptAPI;
}

// 现在Window接口有title和ts两个属性
接口与类型别名:

接口和类型别名很相似,都可以为对象指定类型. 但是区别也是很明显的

  • 接口可以通过继承来拓展自身的类型注解
  • 接口可以通过合并拓展自身的类型注解
  • 类型别名可以为任何类型创建别名,但是接口只适用于对象
ts 复制代码
//结构与类型别名都可以为对象指定类型
type APerosn={
    name:string,
    age:number
}

interface BPerson{
    name:string,
    age:number
}

let a:APerosn={
    name:'张三',
    age:18
}

let b:BPerson={
    name:'张三',
    age:18
}

类(Class)

1.类的定义

TS中也引入了class的语法糖,写法基本和js中的class语法糖相似 不同的是 需要提前定义类中属性与方法的类型注解

ts 复制代码
//ts引入了class语法
class Person{
    name:string;
    age:number;
    //构造函数就是一种方法 所以参数必须规定类型
    constructor(name:string,age:number){
        this.name = name;
        this.age = age;
    }
    sayHi(){
        //因为没有return TS自动做了类型推断 所以这个方法可以不用写void返回类型
        console.log(`大家好,我叫${this.name},今年${this.age}岁`);
    }
    //类的实例方法和对象的方法是一样的 也需要指定类型
    changeName(name:string){
        this.name=name;
    }
}
// 如果类没有类型 会自动默认为any属性
let p1=new Person('张三',18)
2.类的继承

类的继承分为两种 一种是继承父类 另一种是继承接口中的类型定义

类的继承和其他语言面向对象类似,可以重写继承来的方法 也可以省略不写 使用父类继承来的方法. 而接口的继承和jva`是类似的.

ts 复制代码
//ts有两种继承方法 1.extends(继承父类) 2.implements(实现接口 ts特有)
class Animal{
    move(){
        console.log('move');
    }
}

class Dog extends Animal{
    bark(){
        console.log('bark');
    }
}

let dog=new Dog();
dog.bark()

//2.implements  继承interface接口
//和java一样 接口只能定义属性和抽象方法(没有实现的方法)
interface Person{
    name:string;
    age:number;
    move():void
}

class Student implements Person{
    name: string;
    age: number;
    constructor(name:string,age:number){
        this.name=name;
        this.age=age;
    }
    move(){
        console.log('move');
    }
}
3.类成员的可见性

类中的成员属性有四种访问修饰符 不仅仅是修饰属性的 也可以修饰方法

  • public - 公开的,任何人都可以访问(默认)
  • private - 私有的,只能在类内部访问
  • protected - 受保护的,只能在类和子类中访问
  • readonly - 只读的,只能在声明时或构造函数中初始化

演示代码

ts 复制代码
// 基类 - 演示所有四种访问修饰符
class Person {
    // 1. public - 公开的,任何人都可以访问(默认)
    public name: string;
    
    // 2. private - 私有的,只能在类内部访问
    private secret: string;
    
    // 3. protected - 受保护的,只能在类和子类中访问
    protected age: number;
    
    // 4. readonly - 只读的,只能在声明时或构造函数中初始化
    readonly id: number;
    
    constructor(name: string, secret: string, age: number, id: number) {
        this.name = name;
        this.secret = secret;
        this.age = age;
        this.id = id;
    }
    
    // 公共方法可以访问所有成员
    public introduce(): void {
        console.log(`我叫${this.name},年龄${this.age},ID: ${this.id}`);
        // console.log(this.secret); // 可以在类内部访问private成员
    }
    
    // 私有方法只能在类内部调用
    private tellSecret(): void {
        console.log(`我的秘密是: ${this.secret}`);
    }
}

// 子类 - 继承Person类
class Student extends Person {
    public grade: string;
    
    constructor(name: string, secret: string, age: number, id: number, grade: string) {
        super(name, secret, age, id);
        this.grade = grade;
    }
    
    public study(): void {
        console.log(`${this.name}正在学习`);
        // 可以访问protected成员
        console.log(`年龄: ${this.age}`);
        
        // 不能访问private成员 - 会报错
        // console.log(this.secret); // Error: Property 'secret' is private
        
        // 可以访问public成员
        console.log(`ID: ${this.id}`);
    }
}

// 测试代码
const person = new Person('张三', '我喜欢吃糖', 25, 1001);
const student = new Student('李四', '我害怕考试', 18, 1002, '高三');

// 1. public成员 - 可以任意访问
console.log(person.name);     // 输出: 张三
console.log(student.name);   // 输出: 李四

// 2. private成员 - 不能在类外部访问
 console.log(person.secret);  // Error: Property 'secret' is private
 console.log(student.secret); // Error: Property 'secret' is private

// 3. protected成员 - 不能在类外部访问
 console.log(person.age);     // Error: Property 'age' is protected
 console.log(student.age);   // Error: Property 'age' is protected

// 4. readonly成员 - 可以读取但不能修改
console.log(person.id);       // 输出: 1001
// person.id = 1003;          // Error: Cannot assign to 'id' because it is a read-only property

// 调用方法
person.introduce();           // 输出: 我叫张三,年龄25,ID: 1001
student.introduce();          // 输出: 我叫李四,年龄18,ID: 1002
student.study();              // 输出: 李四正在学习\n年龄: 18\nID: 1002

// 尝试修改readonly属性 - 编译时会报错
 student.id = 1005;         
// Error: Cannot assign to 'id' because it is a read-only property

泛型(Generics)

泛型基础概念

钻石运算符 <> 里面添加的是类型变量比如T 这个T是一个变量 往里填哪个类型 T就是什么类型 他是一个类型的容器 可以自动捕获用户提供的类型.

ts 复制代码
// 泛型是可以在保证安全的清况等下 让函数与多种类型一起工作 从而实现复用 常用于函数 接口 类中
// <>叫做钻石运算符 里面添加类型变量 比如T等等 
//T是一个特殊的变量 他的处理类型不是值 他是一个类型的容器 可以自动捕获用户提供的类型
function id<T>(value:T):T{
    return value;
}
const getId=<t>(Value:t):t=>{
    return Value;
}
//调用
const num=<string>getId('123')
const num1=<number>getId(123)

//简化调用 调用泛型函数的时候 可以把尖括号省了 ts会自动识别类型(类型参数推断)
const num2=getId(123);
//有时候推断的类型可能不准确 就需要手动去定义
泛型约束

默认情况下 泛型函数的类型数量type可以代表多个类型 这导致无法访问任何属性 比如id('a')调用函数时参数的长度

ts 复制代码
function id<T>(value:T):T{
    console.log(value.length);
    return value
}

使用上面的函数会报错 因为T可以代表任意类型 无法保证一定存在length属性 此时就需要为泛型添加约束来收缩类型

有两种为泛型添加约束的方法

  • 方法1: 为type指定更具体的类型

    ts 复制代码
    function id<T>(value:T[]):T[]{
      console.log(value.length);
      return value
    }
  • 方法2: 定义接口为T添加约束

    ts 复制代码
    interface LengthWise{
        length:number
    }
    function id<T extends LengthWise>(value:T):T{
        console.log(value.length);
        return value
    }
    • 方法二的解释:
      • 1.创建接口提供需要的属性 比如length
      • 2.通过extends关键字使用该接口 为泛型(控制变量)添加约束
      • 表述为 传入的类型必须具有length属性

多个泛型相互约束:

泛型的类型变量可以存在多个 而且类型变量之间也可以约束的 (比如 第二个类型变量受第一个变量的约束) 比如创建一个函数来获取兑现中属性的值

ts 复制代码
//泛型的类型变量可以存在多个 而且类型变量之间也可以约束的 (比如 第二个类型变量受第一个变量的约束) 比如创建一个函数来获取兑现中属性的值

function getProp<T,K extends keyof T>(Obj:T,key:K):T[K]{
    return Obj[key];
}
let person={name:'jack',age:18}
console.log(getProp(person,'name')); //jack
//keyof关键字会接受一个对象类型 生成其键名称(可能是字符串或是数字)的联合类型
//实例中keyof T实际上获取的是person对象所有键的联合类型 也就是'name'|'age'
//类型变量k受T的约束 可以理解为 k只能是t所有键的任意一个
泛型接口与泛型类
泛型接口:

接口也可以配合泛型使用.

ts 复制代码
// 接口也可以配合泛型来使用 已增加灵活性 增强复用性
interface IdFunc<T>{
    id:(Value:T)=>T
    ids:()=>T[]
}
let Obj:IdFunc<string>={
    id(Value){
        return Value
    },
    ids(){
        return []
    }
}
泛型类:

class 也可以搭配泛型来用. 比如: react的class组件的基类 Component就是泛型 不用的组件有不同的props和state

ts 复制代码
//创建泛型类
class GenericNumber<NumType>{
    defaultvalue: NumType;
    constructor(value: NumType) {
        this.defaultvalue = value;
    }
    add(x: NumType, y: NumType): NumType {
        return (x as any) + (y as any);
    }
}

//如果类存在构造函数并且构造函数正好使用到了类的泛型 就可以省略尖括号

联合类型与交叉类型

联合类型的使用和场景
联合类型与类型别名

为了简化复杂的联合类型,可以使用类型别名:

ts 复制代码
// 定义联合类型别名
type StringOrNumber = string | number;
type Status = "pending" | "approved" | "rejected";

let value: StringOrNumber;
value = 123;
value = "hello";

let status: Status;
status = "pending";   // 正确
status = "approved";  // 正确
status = "done";      // 错误,不在指定的字面量类型中
联合类型与类型守卫

当使用联合类型时,TypeScript只允许访问所有类型共有的属性和方法。要访问特定类型的属性,需要使用类型守卫:

ts 复制代码
function processValue(value: string | number) {
    // 错误:length属性只存在于string类型中
    // console.log(value.length);
    
    // 使用类型守卫
    if (typeof value === "string") {
        // 在这个代码块中,TypeScript知道value是string类型
        console.log(value.length); // 正确
        console.log(value.toUpperCase());
    } else {
        // 在这个代码块中,TypeScript知道value是number类型
        console.log(value.toFixed(2));
    }
}
联合类型与接口

联合类型也可以与接口结合使用:

ts 复制代码
interface Bird {
    type: "bird";
    flyingSpeed: number;
}

interface Horse {
    type: "horse";
    runningSpeed: number;
}

// 联合类型
type Animal = Bird | Horse;

function moveAnimal(animal: Animal) {
    switch (animal.type) {
        case "bird":
            console.log(`Bird flying at speed: ${animal.flyingSpeed}`);
            break;
        case "horse":
            console.log(`Horse running at speed: ${animal.runningSpeed}`);
            break;
    }
}
联合类型与null/undefined

联合类型常用于处理可能为null或undefined的值:

ts 复制代码
// 用户可能未定义
let user: User | null = null;

// 在使用前需要检查
if (user !== null) {
    console.log(user.name); // 安全访问
}

// 或者使用可选链操作符
console.log(user?.name);
联合类型与字面量类型

联合类型与字面量类型结合使用可以创建枚举式的类型:

ts 复制代码
// 方向只能是这四个字符串值之一
type Direction = "up" | "down" | "left" | "right";

function move(direction: Direction) {
    // ...
}

move("up");    // 正确
move("north"); // 错误,不在指定的字面量类型中
联合类型的注意事项
  1. 只能访问共有成员:使用联合类型时,只能访问所有类型共有的属性和方法
  2. 类型守卫:要访问特定类型的属性,需要使用类型守卫进行类型检查
  3. 可读性:对于复杂的联合类型,建议使用类型别名提高可读性
  4. 过度使用:避免过度使用联合类型,可能导致代码难以维护
交叉类型:
1.定义

使用符合& 对两个接口进行组合 成一个新的类型

交叉功能类似于接口的继承 用来组合多个类型为一个类型(一般用在对象类型中)

ts 复制代码
//交叉功能类似于接口继承 用于组合多个类型为一个类型(常用于对象类型)
interface Person{
    name:string
}
interface Contact{
    phone:number;
}
type PersonContact =Person & Contact;
let obj:PersonContact={
    name:'张三',
    phone:123456789 
}
2.接口交叉与继承
  • 相同点: 都可以实现对象类型的组合
  • 不同点:两种方式实现类型组合时 对于同名属性之间处理冲突的方式不同
ts 复制代码
//交叉类型和接口继承的对比
interface A {
    fn:(vlaie:number)=>string;
}

//接口继承 出现这种情况要么接口会报错 要么只保留一个属性
interface B extends A {
    fn(value:string):string
}
//交叉类型
interface A { 
    fn:(value:number)=>string;
}
interface B { 
    fn:(value:string)=>string;
}
type C = A & B;
//可以将组合后的c简单理解为 fn:(value:(number|string))=>string

处理方法: 对于接口继承要么类型会报错 要么只保留两个类型的其中之一 然后对于交叉合成来说 可以两个类型同时保留 类似于联合类型

第四部分:类型系统进阶

类型兼容性

结构化类型系统

ts使用的是结构化的类型系统 如果类的类型定义 是一样的 尽管类名是 不一样的 但是仍然可以当做一个类来看

ts 复制代码
class Point {
    x:number;
    y:number;
    constructor(x:number,y:number) {
        this.x = x;
        this.y = y;
    }
}

class Point2D {
    x:number;
    y:number;
    constructor(x:number,y:number) {
        this.x = x;
        this.y = y;
    }
}

let p1: Point =new Point2D(1,2)  //这种写法是允许的 因为Point2D兼容Point 所以Point和Point2D可以看作是一个类
对象类型兼容
函数类型兼容

类型推断与类型断言

类型推断机制
类型断言的使用场景

映射类型与工具类型

索引签名类型

绝大多数情况下 我们都在使用对象前就确定的对象的结构 但是并未对象添准确的类型 索性签名类型就是为接口中的 索引 都进行类型标注

使用场景:无法确定对象中有哪些类型信息 此时就用索引签名类型

ts 复制代码
interface AnyObject{
  [key:string]:number
}
let obj:AnyObject={
  a:1,
  b:2
}
//解释 使用[key:string] 用来约束接口中出现的属性名 表示只要是 string类型 的属性名称都可以出现在对象中
//:number约束了属性值的类型 表示只要是 number类型 的属性值都可以出现在对象中
//key只是一个占位符 有了[key:String]:number 就可以在对象中定义任意个属性 只要属性名是字符串 属性值是数字即可
//这里的key可以是任意名称
  • 使用[key:string] 用来约束接口中出现的属性名 表示只要是 string类型 的属性名称都可以出现在对象中
  • :number约束了属性值的类型 表示只要是 number类型 的属性值都可以出现在对象中
  • key只是一个占位符 有了[key:String]:number 就可以在对象中定义任意个属性 只要属性名是字符串 属性值是数字即可,这里的key可以是任意名称
映射类型

映射类型就是基于旧类型创建新类型(对象类型) 减少重复 提升开发效率

ts 复制代码
//例子
type Propkeys='x'|'y'|'z'
type Type1={
  x:number;
  y:number;
  z:number;
}
//这样写将x y z重复写了两遍 通常可以使用映射类型来进行简化
type Type2={
  [Key in Propkeys]:number
}

//实际开发还是使用Record类型工具
type Type3=Record<Propkeys,number>;

解释:

  • 映射类型是基于索引签名类型的 所以语法类似于索引签名类型 也是用[]
  • [Key in Propkeys] 表示遍历 Propkeys 中的每个元素 并将其赋值给 Key
  • 映射类型不能用于接口 只能用于类型别名
  • 实际开发还是使用Record泛型工具

对象类型的类型映射

ts 复制代码
type Props={
  a:number;
  b:string;
  c:boolean;
}
type Type={
  [key in keyof Props]:number
}
let obj:Type={
  a:1,
  b:2,
  c:3
}
泛型工具类型
Partial<Type>

用来构造一个类型 将type的所有属性设置为可选

ts 复制代码
interface Props{ //每个类型都是必选的属性 如果需要可选类型需要添加'?'
    id:string;
    children:number[]
}
type PartialProps=Partial<Props>
// 创建的新类型结构和props一模一样 但是所有属性是可选的

 const obj0:Props={
     id:'1',
    // children:[1,2,3]
 }//缺少children属性 就会报错
 
const obj:PartialProps={
    id:'1'
}//可以只写一个属性
Readonly<type>
  • 创建一个只读的类型 不可更改 就不需要单独为属性添加readonly属性

ts 复制代码
type readonlyProps=Readonly<Props>
const obj1:readonlyProps={
    id:'1',
    children:[1,2,3]
}
 obj1.id='2'//不可以修改
Pick<type,keys>

type中选择一组属性来构造新类型

  • pick中有两个类型变量 如果值选择一个则值传入该属性名即可

  • 第二个变量传图的属性只能是第一个类型变量中存在的属性

ts 复制代码
type PickProps=Pick<Props,'id'>
const obj2:PickProps={
    id:'1'
    //children:[1,2,3]//不可以添加 添加就会报错
}
 type PickProps = {
     id: string;
 }
Record<key,type>

构造一个对象类型 属性键为key 属性类型为type

ts 复制代码
type RecordObj=Record<'a'|'b',string>
const obj3:RecordObj={
    a:'1',
    b:'2'
}
//Record工具类型有两个类型变量 1.表示对象有哪些属性 2.表示对象属性对应的类型

第五部分:实用技巧与最佳实践

模块与声明文件

ts文件中 有两种声明文件的方法 一个是后缀为.ts 一个是后缀为.d.ts

  • .ts文件 既包含类型信息又包含可执行代码
  • .d.ts文件 只包含类型信息 不包含可执行代码 用途是为js提供类型信息

类型声明文件概述

在开发的时候会使用很多第三方库 我们不知道这些库是用js写的还是ts写的 所以我们需要类型声明文件为已经存在的js库提供类型信息,这样我们在使用这些库的时候就可以获得类型检查和智能提示

使用第三方库的类型声明

可以使用npm i @types/库名 --save来安装库的类型信息(第三方库)

相关推荐
徐同保1 小时前
使用GitKraken把feature_xtb_1104分支的多次提交记录合并到一起,只保留一次提交记录,并合并到master分支
前端
小光学长2 小时前
基于Vue的智慧楼宇报修平台设计与实现066z15wb(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
前端·数据库·vue.js
醉方休2 小时前
web前端 DSL转换技术
前端
sen_shan2 小时前
Vue3+Vite+TypeScript+Element Plus开发-27.表格页码自定义
前端·javascript·typescript
刺客_Andy2 小时前
React 第五十二节 Router中 useResolvedPath使用详解和注意事项示例
前端·react.js·架构
豆浆9452 小时前
vue3+qiankun主应用和微应用的路由跳转返回
前端
王将近2 小时前
Cesium 山洪流体模拟
前端·cesium
小时前端2 小时前
当循环遇上异步:如何避免 JavaScript 中最常见的性能陷阱?
前端·javascript
Bacon3 小时前
Electron 集成第三方项目
前端