TypeScript 类型兼容性 引用自中文手册

TypeScript结构化类型系统的基本规则是,如果x要兼容y,那么y至少具有与x相同的属性。比如:

interface Named {
    name: string;
}

let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: 'Alice', location: 'Seattle' };
x = y;

这里要检查 y 是否能赋值给 x,编译器检查 x 中的每个属性,看是否能在 y 中也找到对应属性。 在这个例子中,y 必须包含名字是namestring 类型成员。y 满足条件,因此赋值正确。

检查函数参数时使用相同的规则:

function greet(n: Named) {
    alert('Hello, ' + n.name);
}
greet(y); // OK

注意,y 有个额外的location属性,但这不会引发错误。 只有目标类型(这里是Named)的成员会被一一检查是否兼容。
(多的 赋给 少的)

这个比较过程是递归进行的,检查每个成员及子成员。

在这里插入图片描述
报错提示(name 丢失)
在这里插入图片描述

比较两个函数

参数

相对来讲,在比较原始类型和对象类型的时候是比较容易理解的,问题是如何判断两个函数是兼容的。 下面我们从两个简单的函数入手,它们仅是参数列表略有不同:

let x = (a: number) => 0;
let y = (b: number, s: string) => 0;

y = x; // OK
x = y; // Error

要查看 x 是否能赋值给 y ,首先看它们的参数列表。 x 的每个参数必须能在 y 里找到对应类型的参数。 注意的是参数的名字相同与否无所谓,只看它们的类型。 这里,x 的每个参数在 y 中都能找到对应的参数,所以允许赋值。

第二个赋值错误,因为 y 有个必需的第二个参数,但是 x 并没有,所以不允许赋值。

你可能会疑惑为什么允许忽略参数,像例子 y = x 中那样。 原因是忽略额外的参数在JavaScript里是很常见的。 例如,Array#forEach 给回调函数传3个参数:数组元素,索引和整个数组。 尽管如此,传入一个只使用第一个参数的回调函数也是很有用的:

let items = [1, 2, 3];

// Don't force these extra arguments
items.forEach((item, index, array) => console.log(item));

// Should be OK!
items.forEach((item) => console.log(item));

在这里插入图片描述
(少的 赋给 多的)

返回值

下面来看看如何处理返回值类型,创建两个仅是返回值类型不同的函数:

let x = () => ({name: 'Alice'});
let y = () => ({name: 'Alice', location: 'Seattle'});

x = y; // OK
y = x; // Error because x() lacks a location property

类型系统强制源函数的返回值类型必须是目标函数返回值类型的子类型。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可选参数及剩余参数

比较函数兼容性的时候,可选参数与必须参数是可互换的。 源类型上有额外的可选参数不是错误,目标类型的可选参数在源类型里没有对应的参数也不是错误。

当一个函数有剩余参数时,它被当做无限个可选参数。

这对于类型系统来说是不稳定的,但从运行时的角度来看,可选参数一般来说是不强制的,因为对于大多数函数来说相当于传递了一些 undefinded

有一个好的例子,常见的函数接收一个回调函数并用对于程序员来说是可预知的参数但对类型系统来说是不确定的参数来调用:

function invokeLater(args: any[], callback: (...args: any[]) => void) {
    /* ... Invoke callback with 'args' ... */
}

// Unsound - invokeLater "might" provide any number of arguments
invokeLater([1, 2], (x, y) => console.log(x + ', ' + y));

// Confusing (x and y are actually required) and undiscoverable
invokeLater([1, 2], (x?, y?) => console.log(x + ', ' + y));

被赋值的函数中使用:...args: any[]) => void 剩余参数时,赋值的参数可用任意多个参数传递,但类型需要与剩余参数对应

小示例

const getSum = (arr: number[], callback: (...args: number[]) => number): number => {
  return callback(...arr)
}
const res = getSum([1, 2, 3], (...args:number[]): number => args.reduce((a, b) => a + b, 0))
console.log(res);

const res2 = getSum([1, 2, 3],(arg1: number, arg2: number, arg3: number): number => arg1 + arg2 +arg3)
console.log(res2);

函数参数双向协变

函数 A、B 中有相同的参数及类型, A、B 可相互赋值

let funA = (arg: number | string): void => {}
let funB = (arg: number): void => {}
// 函数 A、B 中有相同的参数及类型, A、B 可相互赋值
funA = funB
funB = funA

详细介绍: TypeScript中文手册.

函数重载

对于有重载的函数,源函数的每个重载都要在目标函数上找到对应的函数签名。 这确保了目标函数可以在所有源函数可调用的地方调用。
在这里插入图片描述
在这里插入图片描述

枚举

枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的。比如,
在这里插入图片描述

类与对象字面量和接口差不多,但有一点不同:类有静态部分和实例部分的类型。 比较两个类类型的对象时,只有实例的成员会被比较。 静态成员构造函数 不在比较的范围内。

class Animal {
    feet: number;
    constructor(name: string, numFeet: number) { }
}

class Size {
    feet: number;
    constructor(numFeet: number) { }
}

let a: Animal;
let s: Size;

a = s;  //OK
s = a;  //OK

在这里插入图片描述
在这里插入图片描述

类的私有成员

私有成员会影响兼容性判断(privateprotected)。 当类的实例用来检查兼容时,如果目标类型包含一个私有成员,那么源类型必须包含来自同一个类的这个私有成员。 这允许子类赋值给父类,但是不能赋值给其它有同样类型的类
在这里插入图片描述
在这里插入图片描述

泛型

因为TypeScript是结构性的类型系统,类型参数只影响使用其做为类型一部分的结果类型。比如,

interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // okay, y matches structure of x

上面代码里,xy 是兼容的,因为它们的结构使用类型参数时并没有什么不同。 把这个例子改变一下,增加一个成员,就能看出是如何工作的了:

interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;

x = y;  // error, x and y are not compatible

在这里插入图片描述
在这里,泛型类型在使用时就好比不是一个泛型类型。

对于没指定泛型类型的泛型参数时,会把所有泛型参数当成any比较。 然后用结果类型进行比较,就像上面第一个例子。

比如,

let identity = function<T>(x: T): T {
    // ...
}

let reverse = function<U>(y: U): U {
    // ...
}

identity = reverse;  // Okay because (x: any)=>any matches (y: any)=>any

引用来源: TypeScript 中文手册.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TypeScript具有丰富的类型系统,可以帮助开发者在编码过程中捕获错误、提供代码补全和自动提示等功能。以下是一些常见的 TypeScript 类型: 1. 原始类型: - boolean: 布尔类型,只能是 true 或 false。 - number: 数字类型,包括整数和浮点数。 - string: 字符串类型。 - null 和 undefined: 分别表示空值和未定义值。 2. 数组类型: - 基本数组类型:例如 number[] 表示数字数组,string[] 表示字符串数组。 - 泛型数组类型:使用泛型语法定义特定类型的数组,例如 Array<number> 表示数字数组。 3. 元组类型: - 元组是固定长度和特定类型的数组。 - 例如 [string, number] 表示包含一个字符串和一个数字的元组。 4. 对象类型: - 对象类型可以使用字面量语法定义,例如 { name: string, age: number } 表示一个具有 name 和 age 属的对象。 - 也可以使用接口(interface)或类(class)定义对象类型。 5. 函数类型: - 函数类型包括参数的类型和返回值的类型,例如 (a: number, b: number) => number 表示接收两个数字参数并返回一个数字的函数类型。 6. 枚举类型: - 枚举用于定义一组命名的常量值。 - 例如 enum Color { Red, Green, Blue } 定义了一个颜色的枚举类型,可以通过 Color.Red、Color.Green 等访问枚举值。 除了以上类型TypeScript 还支持联合类型、交叉类型类型别名、类型推断和泛型等高级特,可以更灵活地应对各种编程场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值