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
相关推荐
这个昵称也不能用吗?6 分钟前
React 19 【use】hook使用简介
前端·react.js·前端框架
web小白成长日记8 分钟前
修复 Storybook MDX 中 “does not provide an export named ‘ArgsTable‘” 的实战
前端
Aotman_16 分钟前
Vue <template v-for> key should be placed on the <template> tag.
前端·javascript·vue.js
A_nanda34 分钟前
vue快速学习框架
前端·javascript·vue.js·学习·c#
蜗牛攻城狮35 分钟前
“直接 URL 下载” vs “前端 Blob 下载”:原理、区别与最佳实践
前端·javascript·二进制流
海绵宝宝_37 分钟前
Chrome强开Gemini助手教程
前端·人工智能·chrome
abments44 分钟前
chrome设置启动浏览器后自动打开关闭前的页面
前端·chrome
刘一说1 小时前
Pinia状态持久化的“隐形陷阱“:为什么页面刷新后状态丢失?
前端·javascript·vue.js
心柠1 小时前
webpack
前端·webpack·node.js
C澒1 小时前
前端编码规范
前端·团队开发·代码规范