ts的类型兼容性

核心原则:结构化类型(Duck Typing)

在 TypeScript 中,只要一个类型的结构(即它的成员)兼容,就可以赋值,不管它叫什么名字或从哪里来。

ts兼容的魅力所在:TypeScript 看"结构"不看"出身"。只要你的对象有我需要的属性,你就能当我的变量,哪怕你还会更多技能!

ts 复制代码
interface Point {
    x: number;
    y: number;
}

class SomePoint {
    x: number = 0;
    y: number = 0;
}

const p: Point = new SomePoint(); // ✅ 兼容!结构相同

主要的类型兼容性规则

1. 对象类型兼容性:目标类型(Target) vs 源类型(Source)

当把一个对象赋值给另一个对象类型时,TS 检查:

源类型必须包含目标类型的所有成员(可以更多,但不能少)。 成员的类型也必须兼容。

ts 复制代码
interface Named {
  name: string;
}

interface Person {
  name: string;
  age: number;
}

const person: Person = { name: "Alice", age: 25 };

console.log(person);

const named: Named = person; // ✅ 兼容!Person 有 name 属性
console.log(named);

2. 函数类型兼容性

函数兼容性比较复杂,涉及 参数返回值

(1) 参数:目标函数的参数数量 ≤ 源函数的参数数量(逆变)
  • 目标函数(你期望的)参数越少,越容易兼容。
  • 源函数(你提供的)可以忽略多余的参数。
ts 复制代码
type Handler = (a: number, b: number) => void;
const h: Handler = (x, y) => { /* ... */ };

// 可以赋值一个参数更少的函数
const f1 = (x: number) => { /* ... */ };
h = f1; // ✅ 兼容!f1 可以忽略第二个参数

// 但不能赋值一个参数更多的函数
const f2 = (x: number, y: number, z: number) => { /* ... */ };
h = f2; //  不兼容!h 调用时不会传 z
2) 返回值:源函数的返回值类型必须 ≥ 目标函数的返回值类型(协变)
  • 源函数可以返回"更多"信息(子类型)。
ts 复制代码
interface Animal { name: string }
interface Dog extends Animal { breed: string }

type Getter = () => Animal;
const getAnimal: Getter = () => ({ name: "Pet" });

// 可以返回更具体的类型
const getDog = () => ({ name: "Rex", breed: "Lab" });
getAnimal = getDog; //  兼容!Dog 是 Animal 的子类型
3. 可选属性和额外属性
  • 可选属性:目标类型中的可选属性,源类型可以没有。
  • 额外属性 :源类型可以有目标类型没有的属性(但直接对象字面量有严格检查)。
ts 复制代码
interface Config {
    url: string;
    method?: string; // 可选
}

const config: Config = { url: "http://example.com" }; // method 可选

但是,对象字面量有严格属性检查:

ts 复制代码
const bad = { url: "http://", timeout: 5000 };
const config2: Config = bad; //OK,因为 bad 是变量
const config3: Config = { url: "http://", timeout: 5000 }; // 错误!字面量不能有额外属性
4. 枚举兼容性
  • 枚举之间不兼容,即使值相同。
  • 枚举与数字兼容(双向)。
ts 复制代码
enum Color { Red, Green }
enum Size { Small, Large }

let c: Color = Color.Red;
// c = Size.Small; //  不兼容!虽然是 0

let num: number = Color.Red; //  枚举 → number
c = 1; //  number → 枚举
5. 类的兼容性
  • 类的实例类型兼容性基于实例成员不考虑构造函数和静态成员
  • 私有成员(private)和受保护成员(protected)会影响兼容性:只有来自同一个基类的类才兼容。
ts 复制代码
class Animal { private name: string = "" }
class Dog { private name: string = "" }

let a: Animal;
let d: Dog;

// a = d; //  不兼容!即使结构相同,私有成员来自不同类
6. 泛型兼容性

泛型的兼容性取决于类型参数是否被使用。

ts 复制代码
interface Box<T> {
    value: T;
}

let box1: Box<string> = { value: "hello" };
let box2: Box<number> = { value: 123 };

// box1 = box2; // 不兼容!string 和 number 不同

但如果泛型未被使用,可能兼容:

ts 复制代码
interface Empty<T> {}
let empty1: Empty<string>;
let empty2: Empty<number>;

empty1 = empty2; //  兼容!Empty<T> 是"协变"的,且结构相同
7. any 的特殊性
  • any 与其他所有类型都兼容(双向)。
  • 这是类型安全的"逃生舱",但应尽量避免。
ts 复制代码
let anything: any = 123;
anything = "hello";
let str: string = anything; //  any → string
相关推荐
wordbaby20 分钟前
TanStack Router 实战:如何优雅地实现后台管理系统的“多页签” (TabList) 功能
前端·react.js
凌览32 分钟前
2026年1月编程语言排行榜|C#拿下年度语言,Python稳居第一
前端·后端·程序员
user861581857815436 分钟前
Element UI 表格 show-overflow-tooltip 长文本导致闪烁的根本原因与解法
前端
不会写前端的小丁40 分钟前
前端首屏渲染性能优化小技巧
前端
晴虹41 分钟前
lecen:一个更好的开源可视化系统搭建项目--组件和功能按钮的权限控制--全低代码|所见即所得|利用可视化设计器构建你的应用系统-做一
前端·后端·低代码
爱分享的鱼鱼43 分钟前
Pinia 深度解析:现代Vue应用状态管理最佳实践
前端·后端
花归去1 小时前
echarts 柱状图包含右侧进度
开发语言·前端·javascript
多看书少吃饭1 小时前
Vite开发环境按需编译是怎么实现的
前端
ybb_ymm1 小时前
@Async修饰不生效
java·前端·数据库
Sapphire~1 小时前
Vue3-03 熟悉src文件夹及Vue文件格式
前端·javascript·vue.js