接口定义 Interface
接口:对复杂的对象类型进行标注的一种方式,接口是一种类型 ,不能作为值使用。
基础语法定义结构
interface Point {
x: number;
y: number;
}
let p1 = Point; //错误
let p1: Point = { x: 100, y: 100 };
可选属性
interface Point {
x: number;
y: number;
color?: string; //该属性是可选的
}
只读属性
interface Point {
readonly x: number;//除了初始化以外,不能被再次赋值
readonly y: number; //除了初始化以外,不能被再次赋值
}
任意属性
索引只可以是数字或字符串
若同时存在,数字类型的值类型必须是字符串类型的值类型或子类型
interface Point1 {
[prop1: string]: string;
[prop2: number]: number; // 错误
}
interface Point2 {
[prop1: string]: Object;
[prop2: number]: Date; // 正确
}
使用接口描述函数
interface IFunc {
(x: number, y: number): number
}
let fn1: IFunc = function(a: number, b: number): number {
return a + b;
}
let fn12: IFunc = function(a: number, b: number): number {
return a + b;
}
接口合并
相同命名的接口会自动合并
如果存在同名的非函数成员,则必须保证他们类型一致,否则编译报错
接口中的同名函数则是采用重载。
interface Box {
height: number;
width: number;
fn(a: string): string;
}
interface Box {
height: number;
scale: number;
// width: string; //若放开,会因为和上一个interface定义不一致而报错
fn(a: number): number;
}
let box: Box = {
height: 5,
width: 6,
scale: 10,
fn: function(a:any):any {
return a;
},
}
联合类型
也可以称为多选类型,当我们希望标注一个变量为多个类型之一时可以选择联合类型标注,或的关系
function fn(value: string | number) {
return value
}
fn(1)
fn('1')
fn(true)//error
交叉类型
也可以称为合并类型,可以把多种类型合并到一起成为一种新的类型,并且的关系
interface o1 { x: number; y: string }
interface o2 { z: number }
let obj1: o1 & o2 = { x: 1, y: 'zmouse' } //error
let obj2: o1 & o2 = { x: 1, y: 'zmouse', z: 2 }
字面量类型
有的时候,我们希望标注的不是某个类型,而是一些固定值,就可以使用字面量类型,配合联合类型会更有用
function fn(direction: 'left' | 'right') {
return direction
}
fn('left')
fn('right')
fn('top')//error
类型别名type
有的时候类型标注比较复杂,这个时候我们可以类型标注起一个相对简单的名字
type direction = 'left' | 'right'
function fn(direction: direction) {
return direction
}
使用类型别名定义函数类型
type callback = (a: string) => string;
let fn1: callback = (a) => a;
// 或者直接
let fn2: (a: string) => string = (a) => a;
interface 与 type 的区别
interface
只能描述 object / class / function 的类型
同名 interface 自动合并,利于扩展
type
不能重名
能描述所有数据
类型断言
有的时候,我们可能标注一个更加精确的类型,或者确定某个元素类型,以使用某些属性。
img 的默认推断类型为 Element,只是元素类型的通用类型,如果我们去访问 src 这个属性是有问题的。
我们需要把它的类型标注得更为精确:HTMLImageElement 类型,这个时候,我们就可以使用类型断言,
它类似于一种类型转换,但只是一种预判,并不会数据本身产生实际的作用
let img = document.querySelector('#img');
img && img.src//类型“Element”上不存在属性“src”
//修改方案一
let img = document.querySelector('#img');
img && (<HTMLImageElement>img).src;
(img as HTMLImageElement).src;
//修改方案二
let img = <HTMLImageElement>document.querySelector('#img');
let img = document.querySelector('#img') as HTMLImageElement;
img && img.src
类型操作
typeof
在 TypeScript 中,typeof获取数据的类型,也可以用来生成类型标注。
let str1 = 'TypeScript';
// 获取数据类型,程序运行时也存在
let t = typeof str1;
// type 声明的是类型名称,只是在ts编译检测阶段使用
type myType = typeof str1;
let a = t;
let b: myType = 'TypeScript2';
keyof
interface Person {
name: string;
age: number;
};
type personKeys = keyof Person;// 等同:type personKeys = "name" | "age"
let p1:Person = {
name: 'zMouse',
age: 35
}
function getPersonVal(k: keyof typeof p1) {
return p1[k];
}
in
interface Person {
name: string;
age: number;
}
type personKeys = keyof Person;
type newPerson = {
[k in personKeys]: string
// 等同 [k in 'name'|'age']: number;
// 也可以写成
// [k in keyof Person]: number;
}
// 等于是
// type newPerson = {
// name: number;
// age: number;
// }
自定义类型保护
如下情况会出现报错
function fn2(elements: number[] | number) {
if (elements.length) { //类型“Element”上不存在属性“length”
elements.forEach((el: number) => {
console.log(el);
});
} else {
console.log(elements);
}
}
可以增加一个自定义类型保护函数,格式为: xx is XX
function canEach(data: any): data is number[] {
return data.forEach !== undefined;
}
function fn2(elements: number[] | number) {
if (canEach(elements)) {//使用自定义保护函数
elements.forEach((el: number) => {
console.log(el);
});
} else {
console.log(elements);
}
}
类型兼容
TypeScript 的类型系统是基于结构⼦类型的,只要具有相同子类型的成员,则两种类型即为兼容的。
class Person {
name: string;
age: number;
}
class Cat {
name: string;
age: number;
}
function fn(p: Person) {
p.name;
}
let xiaohua = new Cat();
fn(xiaohua);// ok,因为 Cat 类型的结构与 Person 类型的结构相似,所以它们是兼容的