这篇文章讲解类型。莫要让 TypeScript 成为 AnyScript,学习类型也是从简单到复杂。
范型参数变量常用理解
T表示 type 类型P表示 props 属性K/Vkey/valueU常用的类型变量
类型中的关键字和操作符
复杂类型中常用的关键字和操作符
keyof相当于 Object.keysin相当于 for-in?可选操作符-?可选取反extendsinterface/class表示继承type中表示约束- ...
[]- 类型属性访问
- 类型计算
签名
定义函数的输入和输出
严格的类型检查
为了更好的使用 TypeScript, 需要开启严格的 TypeScript 代码检查:
noImplicitAny没有隐式的 any 类型推断- 错误提示:Parameter 'a' implicitly has an 'any' type.ts(7006)
strictNullChecks严格的 null 检查- 默认情况下,值 null 和 undefined 可分配给任何其他类型。
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
}
}
严格的 null 检查 最佳实践1 , 在处理空指针的情况下,非常实用。
type User = {
name: string;
age?: number; // 可选的属性,不存在时,不能使用其上面的属性和方法
}
const getUser = (user: User) => {
console.log(user.name, user.age.toString()) // user.age 🐛 可能不存在但是调用方法
}
age 是可选的,但是在 console.log 中调用了 toString 方法。
- ts: Object is possibly 'undefined'.ts(2532) 我们可以使用
?.来解决这个🐛,严格检查
console.log(user.name, user.age?.toString())
基础类型
- boolean 布尔类型
- string 字符串类型
- void/null/undefined 三种空类型
- any/
any[]类型 - tuple 元祖类型
- 枚举类型
- never/unknown
- 函数类型
- 函数/构造函数字面量类型
- 函数调用类型
- 构造函数签名
按级别划分类型
顶级类型 top type
- any/unknown. 包含所有类型的集合。
底部类型 bottom type
- never 空集合
从类型 type 引出接口 interface
在 TypeScript 中,使用 type 关键字,定义类型别名
type User = number | string; // 定义 number | string 的联合类型为 User
⚠️:别名 type 一旦定义定义就不能扩展自己,只能用 type 定一个其他的类型。基于别名 type 自生不可扩展,引出 接口 interface 的概念就很合适了。
interface Global {
api1: number
}
我们想要扩展 Global 接口,就可以直接重新定义(重新定义不是简单的覆盖行为)
interface Global {
api2: number
}
api2接口属性是补充在 Global 属性上面的,而不是覆盖之前定义的 api1。
TypeScript 对象类型 Object Types
对象类型应该是我们最常用的复合类型在之一了。
定义对象类型的方式:
- interface
- type
他们的最主要的区别之一就是:能不能不产生新的类型的基础上扩展自己。
可选/只读的类型属性
- 可选:
?.<your option> - 只读:
readonly <you option>
索引类型
有时候,我们不确定属性的名称,此时我们就可以定义索引类型
interface User {
[index: string]: any
}
index 表示索引,索引的类型是 string,属性对应的类型是 any
interface 的类型扩展
- 重新定义接口,然后补充新的属性,
不产生一个新的接口 - 使用 extend 关键字补充,
产生一个新的接口
type 的类型补充
- 使用
|联合类型,进行扩展,产生一个新的类型。
交叉类型
两个类型交叉,将两个不同类型组合成一个新的类型没有重复的新的类型
type A = {
name: string
age: number
}
type B = {
age: number
sex: string
}
const C: A & B = {
name: 'sdfd',
age: 123,
sex: 'male'
}
const D: A | B = {
name: 'sdf',
age: 123
}
重要关键字
- extends 赋值(也可以叫分配) 。 广泛应用于条件类型,是高级类型的基石。
- keyof 获取索引值,并组合成新的索引值联合类型。
类型进阶
- 泛型
- 联合类型
- 交叉类型
- 索引类型
- 映射类型
- 条件类型
范型
范型,有一个广义上的类型,但是这个类型具体还不确定,由于不确定所有可以是多种类型。是一对多的关系。常使用 T, U 表示范型,常用于函数参数的约束。
function fn<T>(a: T) {};
T 就范型类型,范型类型确定后,fn 函数参数的类型就确定了。 T 可以对应多个类型。
联合类型
开始可以粗糙的理解为: 数学中的并集。联合类型使用 |操作符来联合类型
type A = string | number; // A 就是 string 和 number 的联合类型,A 可以是 string 也可以是 number;
注意,黏合类型在切换类型的时候,整个值切换
type B = boolean | {z: string};
let binst: B = {z: 123};
binst = false;
// 整个切换之后就能在访问 z 属性了。
交叉类型
开始可以粗糙的理解为: 数学中的交集。联合类型使用 &操作符来交叉类型
type X = string & number; // never 我们知道 string 和 number 类型是没有相交的可能性的
所以交叉类型常用于一下相对复杂点的类型.
type C = string | number;
type D = string | Function
type CxD = C & D; // string
// 示例
const h: CxD = 'this is cxd type'; // 类型检查成功
const hF: CxD = 12312; // 类型检查失败
const gF: CxD = () => {}; // 类型检查失败
索引类型
类型中也有索引操作,获取索引,设置索引
- keyof 获取类型索引
- 使用
[]来定义索引类型和索引值类型
// 访问索引
type INN = {
a: string;
b: string;
};
const kea: keyof INN = "a";
const keb: keyof INN = "b";
const kec: keyof INN = "c"; // 类型检查出错 没有 c 索引
type INN = {
a: string;
b: string;
[index: string]: any; // 添加字符串的索引
};
const kea: keyof INN = "a";
const keb: keyof INN = "b";
const kec: keyof INN = "c"; // 类型检查不会出错,因为又添加字符串的索引
条件类型
与 js 的三元表达式相似。常与 extends 关键字以及范型配合使用形成高级类型。
T extends U ? X : Y;
理解: T 赋值给 U 之后正确吗?正确取 X 类型,否则取 Y 类型。
因为 extends 关键字是 TypeScript 高级类型的基石。所以下面会啰嗦一下,用 js 类型打好基础:
// string
type StrT = "abc" extends string ? "StrTrue" : "StrFalse"; // StrTrue
type StrF = "abc" extends unknown ? "StrTrue" : "StrFalse"; // StrFalse
// boolean
type BoolT = true extends boolean ? "BoolTrue" : "BoolFalse"; // BoolTrue
type BoolF = true extends unknown ? "BoolTrue" : "BoolFalse"; // BoolFalse
// number
type NumT = 123 extends number ? "NumTrue" : "NumFalse"; // NumTrue
type NumF = 123 extends unknown ? "NumTrue" : "NumFalse"; // NumFalse
// null
type NullT = null extends null ? "NullTrue" : "NullFalse"; // NullTrue
type NullT1 = null extends unknown ? "NullTrue" : "NullFalse"; // NullTrue
type NullT2 = null extends any ? "NullTrue" : "NullFalse"; // NullTrue
type NullT3 = null extends void ? "NullTrue" : "NullFalse"; // NullFalse
type NullT4 = null extends boolean ? "NullTrue" : "NullFalse"; // NullFalse
// ...
// object
type ObjT = {} extends Object ? "ObjTrue" : "ObjFalse"; // ObjTrue
type ObjT1 = {} extends unknown ? "ObjTrue" : "ObjFalse"; // ObjTrue
type ObjT2 = {} extends any ? "ObjTrue" : "ObjFalse"; // ObjTrue
type ObjT3 = {} extends void ? "ObjTrue" : "ObjFalse"; // ObjFalse
type ObjT4 = {} extends boolean ? "ObjTrue" : "ObjFalse"; // ObjFalse
type ObjT5 = {} extends null ? "ObjTrue" : "ObjFalse"; // ObjFalse