第四章 接口和类型兼容性

接口

TS中的接口(Interface)是用于约束类、对象、函数的规范

接口定义语句不会出现在编译结果中

约束对象

使用Interface关键字定义接口

ts 复制代码
// 定义用户接口,要求每一个用户对象中必须包含一个name字符串属性和一个age数字属性
interface User {
    name: string
    age: number
}

类似于对象类型约束

ts 复制代码
type User = {
       name: string,
       age: number
}

约束函数

函数可以是单独的函数,也可以作为对象的属性,即方法

约束单独的函数:

ts 复制代码
interface Condition {					// 这里的大括号是划分接口定义代码的符号,不是要求Condition为对象
    (n: number): boolean
}

function sum(nums:number[], callback: Condition){
    let result = 0;
    nums.forEach((num)=>{
        if(callback(num)){
            result += num;
        }
	});
}

类似于

ts 复制代码
// 定义sum函数,函数接收一个数字数组和一个回调函数,数组元素只有满足回调函数中的条件时,才会参与求和

type Condition = (n:number)=>boolean;

// 下面同上
// type Condition = {
//     (n: number): boolean
// }

function sum(nums:number[], callback: Condition){}

约束方法:

ts 复制代码
interface User {
    // sayHello是一个函数(方法),方法不接收任何参数,不返回任何值
    sayHello: ()=>void
    // 等同下面的写法
    sayHello():void
}

继承

接口继承了其他接口,子接口中会自动包含父接口的字段类型限制

ts 复制代码
interface A {
    a: number
}

interface B extends A {
    b: string
}

let b: B = {
    a: 1,
    b: "2"
}

接口允许多继承

ts 复制代码
interface A {
    a: number
}

interface B {
    b: string
}

interface C extends A, B {
    c: boolean
}

let c: C = {
    a: 1,
    b: "2",
    c: true
}

子接口中不允许出现和父接口同名且类型不同的字段

ts 复制代码
interface A {
    a: number
}

interface B {
    b: string
}

interface C extends A, B {
    a: string						// 与父接口A中的a字段类型冲突,不允许
    b: string						// 与付接口B中的b字段类型不冲突,允许
    c: boolean
}

let c: C = {
    a: 1,
    b: "2",
    c: true
}

可选属性

约束属性时加上?,表示该属性可以为undefined

ts 复制代码
interface User {
	name: string			// 必须存在
    height?: number			// 等同于 height: number|undefined
}

readonly

被readonly修饰符修饰的目标是只读的

支持修饰对象成员以及数组、元组类型

修饰成员,成员初始化后就不允许被修改

ts 复制代码
interface User {
	readonly id: string
}

const user = {
    id: "1"
};

user.id = "2";			// ❌️

修饰数组&元组字面量类型,引用类型中的内容就不允许修改

ts 复制代码
const arr: readonly number[] = [1, 2, 3];
// 等同于
const arr: ReadonlyArray<number> = [1, 2, 3];

arr.push();				// ❌️
arr[0] = -1;			// ❌️

只读修饰符只参与编译,不会出现在编译结果中

类型兼容性

如果一个变量(或字面量)能够赋值给另一个变量,则说明这两者的类型是兼容的

不同类型的兼容性策略有所不同:

  • 基本类型:完全匹配,例如不能将boolean类型的值赋值给被string类型约束的变量

  • 对象类型:引用类型的类型兼容型根据赋的值的不同有不同的处理

    将一个变量赋值给另一个变量时,会采用子结构辩型法进行类型判断,目标类型包含一些特征,如果被赋予的值满足了所有特征,那么两者就是兼容的

    ts 复制代码
    interface Duck {
        sound: "嘎嘎嘎",				// 特征1: 拥有sound属性,为"嘎嘎嘎"字面量类型
        swim(): void					// 特征2: 拥有swim方法,方法不接收参数也不返回内容
    }
    
    const person = {
        name: "伪装成鸭子的人",
        age: 20,
        sound: "嘎嘎嘎" as "嘎嘎嘎",				// 类型断言
        swim(){
            console.log(this.name + "正在游泳,并发出了【" + this.sound + "】的声音");
        }
    }
    
    const duck:Duck = person;					// 因为person满足Duck类型的所有特征,因此允许将person赋值给duck

    直接将字面量赋值给变量时,会进行更加严格的检查

    ts 复制代码
    interface Duck {
        sound: "嘎嘎嘎",
        swim(): void
    }
    
    const duck:Duck = {							// 虽然对象字面量满足Duck类型的所有特征,但由于是字面量赋值,所以不允许字面量中出现sound和swim以外的其他属性
        name: "伪装成鸭子的人",
        age: 20,
        sound: "嘎嘎嘎" as "嘎嘎嘎",
        swim(){
            console.log(this.name + "正在游泳,并发出了【" + this.sound + "】的声音");
        }
    }

    使用字面量赋值一定是有意为之的,因为编码是一定知道变量的类型和值的类型具体类型,此时就应该更严格地进行约束

    而第三方变量可能是接口获取或其他方式获取得到的,第三方变量可能并不能确定被赋值的变量的类型,因此降低了约束范围

  • 函数类型:函数类型的约束主要体现在两个方面,参数和返回值

    参数:被赋值的函数字面量的形参数量可以少于(但不能多)函数类型规定的形参数量(这也符合的开发习惯,因为并不是所有的参数都一定需要使用到)

    ts 复制代码
    interface Condition {
        (num: number, index: number): boolean
    }
    
    function sum(nums, callback: Condition){
    	let result = 0;
        nums.forEach((num, index)=>{
            if(callback(num, index)){
                result += num;
            }
        });
        return result;
    }
    
    sum([1, 2, 3, 4, 5], (num)=>{					// 正确
        return num > 3;
    });
    
    sum([1, 2, 3, 4, 5], (num, index)=>{			// 正确
        return index > 3;
    });

    返回值:如果不要求返回内容,则函数定义时可返回也可不返回;如果要求返回内容,则实际的返回值必须满足返回类型的要求

类型断言

如果确定某个变量的值属于某个类型,但是TS对该变量的类型的推断又有问题,那么就可以使用类型断言

ts 复制代码
type Test = "test";

const str = "test";
// 正常情况下,TS会识别str为字符串类型,但实际上它是"test"字面量类型,此时就可以使用类型断言
const str = "test" as Test;
// 或者
const str = <Test>"test";
相关推荐
hunterandroid1 小时前
[鸿蒙从零到一] HarmonyOS 资源管理与多语言适配实战
前端
用户69371750013841 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端
无人生还1 小时前
第 1 篇:从 Vue3 到 React 的思维转变
前端·vue.js·react.js
爱分享的程序猿-Clark2 小时前
【前端分享】vue开发项目,如何实现 从A页面跳转到B页面,并传值!
前端·javascript·vue.js
breeze jiang2 小时前
从原生事件到 React 组件化:用状态驱动模型加载进度 UI
前端·react.js·ui
徐小夕2 小时前
花了3天,我写了一款开源AI公众号编辑器
前端·vue.js·github
PBitW2 小时前
git 中容易遗忘的点 (二) ⚡⚡⚡
前端·git·面试
Hilaku2 小时前
2026 年前端大洗牌:React 服务端到 Vite 的底层重构
前端·javascript·程序员
PBitW3 小时前
git 中容易遗忘的点 (三) 🚀🚀🚀
前端·git·面试