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
相关推荐
hellokai41 分钟前
React Native新架构源码分析
android·前端·react native
li理1 小时前
鸿蒙应用开发完全指南:深度解析UIAbility、页面与导航的生命周期
前端·harmonyos
去伪存真1 小时前
因为rolldown-vite比vite打包速度快, 所以必须把rolldown-vite在项目中用起来🤺
前端
KubeSphere1 小时前
Kubernetes v1.34 重磅发布:调度更快,安全更强,AI 资源管理全面进化
前端
wifi歪f1 小时前
🎉 Stenciljs,一个Web Components框架新体验
前端·javascript
1024小神2 小时前
如何快速copy复制一个网站,或是将网站本地静态化访问
前端
掘金一周2 小时前
DeepSeek删豆包冲上热搜,大模型世子之争演都不演了 | 掘金一周 8.28
前端·人工智能·后端
moyu842 小时前
前端存储三剑客:Cookie、LocalStorage 与 SessionStorage 全方位解析
前端
不爱说话郭德纲2 小时前
👩‍💼产品姐一句小优化,让我给上百个列表加上一个动态实时计算高度的方法😿😿
前端·vue.js·性能优化
现在没有牛仔了2 小时前
小试牛刀,用electron+vue3做了一个文件归纳程序~
前端·electron