TypeScript核心
1.接口 interface
基本使用
掌握:使用 interface 声明对象类型
接口声明是命名对象类型的另一种方式
// 通过interface定义对象类型
interface Person {
name: string;
age: number;
sayHi: () => void;
}
// 使用类型
let person: Person = {
name: 'jack',
age: 19,
sayHi(){}
}
interface 后面是接口名称,和类型别名的意思一样。
指定 接口名称 作为变量的类型使用。
接口的每一行只能有 一个 属性或方法,每一行不需要加分号。
2.interface 继承
有两个接口,有相同的属性或者函数,如何提高代码复用?
interface Point2D {
x:number;
y:number;
}
interface Point3D {
x:number;
y:number;
z:number;
}
继承:
相同的属性或展示可以抽离出来,然后使用 extends 实现继承复用
interface Point2D {
x:number;
y:number;
}
// 继承Point2D
interface Point3D extends Point2D {
z:number;
}
// 继承后point3D的结构:{x:number; y:number; z:number}
接口继承的语法:interface 接口A extends 接口B {}
继承后 接口A 拥有 接口B 的所有属性和函数的类型声明
3.type 交叉类型
实现 Point2D 与 {z: number} 类型合并得到 Ponit3D 类型
// 使用type来定义 Point2D 和 Point3D
type Point2D = {
x:number;
y:number;
}
// 使用交叉类型来实现接口继承的功能:
// 使用交叉类型后,Point3D === {x:number; y:number; z:number}
type Point3D = Point2D & {
z: number;
}
let o: Point3D = {
x:1,
y:2,
z:3
}
使用 & 可以合并连接的对象类型,也叫:交叉类型
4.interface vs type
interface 和 type 的相同点和区别
类型别名和接口非常相似,在许多情况下,可以在它们之间自由选择。
接口的几乎所有特性都以类型的形式可用,关键的区别在于不能重新打开类型以添加新属性,而接口总是可扩展的。
不同的点:
type 不可重复定义
type Person = {
name: string;
}
// 标识符“Person” 重复 Error
type Person = {
age: number;
}
interface 重复定义会合并
interface Person {
name: string;
}
interface Person {
age: number;
}
// 类型会合并,注意:属性类型和方法类型不能重复定义
const p: Person = {
name: 'jack',
age:18
}
它们都可以定义对象类型
它们都可以复用,interface 使用 extends , type 使用 &
type 不能重复定义,interface 可以重复会合并
5.类型推断
在 TS 中存在类型推断机制,在没有指定类型的情况下,TS 也会给变量提供类型。
发生类型推断的几个场景场景:
声明变量并初始化时
// 变量age的类型被自动推断为:number
let age = 18;
决定函数返回值时
// 函数返回值的类型被自动推断为: number
const add = (num1:number,num2:number) =>{
return num1 + num2
}
6.字面量类型
字面量类型介绍
js 字面量如:18 ‘jack’ [‘a’] {age: 10} 等等。
使用 js字面量 作为变量类型,这种类型就是字面量类型。
// : 'jack' 是字面量类型
let name: 'jack' = 'jack';
// : 18 是字面量类型
let age: 18 = 18;
// 报错:不能将类型'19'分配给类型'18'
age = 19;
7.字面量类型应用
例如:性别只能是 男 和 女,不会出现其他值。
type Gender = '男' | '女'
let gender: Gender = '男'
gender = '女'
字面量类型配合联合类型来使用,表示:一组明确的可选的值
优势:相比于 string 类型,使用字面量类型更加精确、严谨
8.any 类型
any 类型的作用是逃避 TS 的类型检查
显式any情况:当变量的类型指定为 any 的时候,不会有任何错误,也不会有代码提示,TS会忽略类型检查
let obj:any = {age:18}
obj.bar = 100
obj()
const n: number = obj
以上的代码虽然没有报错提示,但是将来是可能出现错误的。
隐式any的情况:声明变量不给类型或初始值,函数参数不给类型或初始值
// 声明变量不给类型或初始值
let a;
// 函数参数不给类型或初始值
const fn = (n) => {}
any 的使用越多,程序可能出现的漏洞越多,因此不推荐使用 any 类型,尽量避免使用。
9.类型断言
有时候你会比 TS 更加明确一个值的类型,此时,可以使用类型断言来指定更具体的类型。 比如,
// aLink的类型HTMLElement,该类型只包含所有标签公共的属性或方法
// 这个类型太宽泛,没包含a元素特有的属性或方法,如href
const aLink = document.getElementById('link')
但是我们明确知道获取的是一个 A 元素,可以通过 类型断言 给它指定一个更具体的类型。
const aLink = document.getElementById('link') as HTMLAnchorElement
解释:
1.使用 as 关键字实现类型断言
2.关键字 as 后面的类型是一个更加具体的类型(HTMLAnchorElement 是 HTMLElement 的子类型)
3.通过类型断言,aLink 的类型变得更加具体,这样就可以访问 a 标签特有的属性或方法了
10.泛型
泛型别名
// 对后台返回的数据进行类型定义
type User = {
name: string;
age: number;
}
type Goods = {
id: number;
goodsName: string;
}
type Data<T> = {
msg: string;
code: number;
data: T
}
// 使用类型
type UserData = Data<User>
type GoodsData = Data<Goods>
泛型:定义类型别名后加上<类型参数> 就是泛型语法, 使用的时候传入具体的类型即可
T是一个变量,可以随意命名,建议遵循大驼峰即可。
和类型别名配合,在类型别名后加上泛型语法,然后类型别名内就可以使用这个类型参数
泛型可以提高类型的复用性和灵活性
11.泛型接口
// 对象,获取单个ID函数,获取所有ID函数,ID的类型肯定是一致的,但是可能是数字可能是字符串
interface IdFn<T>{
id:() => T;
ids:()=> T[];
}
const idObj: IdFn<number> = {
id() {return 1},
ids() {return [1,2]}
}
在接口名称的后面添加 <类型变量>,那么,这个接口就变成了泛型接口,接口中所有成员都可以使用类型变量。
内置的泛型接口:
const arr = [1,2,3];
// TS有自动类型推断,其实可以看做:const arr: Array<number> = [1,2,3]
arr.push(4);
arr.forEach((item)=> console.log(item));
可以通过 Ctrl + 鼠标左键(Mac:Command + 鼠标左键) 去查看内置的泛型接口
12.泛型函数
// 函数的参数是什么类型,返回值就是什么类型
function getId<T>(id:T):T{
return id
}
let id1 = getId<number>(1)
let id2 = getId('2')
// TS会进行类型推断,参数的类型作为泛型的类型 getId<string>('2')
泛型函数语法?
函数名称后加上 , T是类型参数,是个类型变量,命名建议遵循大驼峰即可。
T 什么时候确定?
当你调用函数的时候,传入具体的类型,T 或捕获到这个类型,函数任何位置均可使用。
泛型函数好处?
让函数可以支持不同类型(复用),且保证类型是安全的。
调用函数,什么时候可以省略泛型?
传入的数据可以推断出你想要的类型,就可以省略。