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来安装库的类型信息(第三方库)

相关推荐
崔庆才丨静觅3 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60614 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了4 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅4 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅4 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅5 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment5 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅5 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊5 小时前
jwt介绍
前端
爱敲代码的小鱼5 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax