TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
下面是一个简单的接口使用小例子,让我们初识接口
interface LabelledValue {label: string;}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
printLabel({size: 10}); // 直接传参,是要严格遵守LabelledValue接口的条件
printLabel({size: 10, label: "Size 10 Object"});// 报错,接口中没有size,是不被允许的
那么要如何绕开检查,添加一个size属性呢(只要传入的对象满足接口提到的必要条件,那么它就是被允许的)
①将这个对象赋值给一个另一个变量,因为myObj变量不会经过额外属性检查
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj); // 正确
②使用断言(告诉编辑器, as 前面的类型就是LabelledValue类型)
printLabel({size: 10, label: "Size 10 Object"} as LabelledValue);
③在前面加一个类型约束,跟类型断言是一个道理
printLabel({size: 10, label: "Size 10 Object"});
④.字符串索引签名;
在接口LabelledValue 里面添加一个[m:string]:any,告诉编辑器,可以添加任意的属性
interface LabelledValue {label: string;[m:string]:any;}
此时运行
printLabel({size: 10, label: "Size 10 Object"}); // 正确
接口里的属性不一定都是必需的,有些是只在某些条件下存在,或者根本不存在,此时我们就要用到可选属性了
interface SquareConfig {
width: number;
color?: string;
}
const square1:SquareConfig = {width:100}; // 由于color是可选属性,所以是被允许
const square2:SquareConfig = {width:100,color:'#ff6600'}
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly
来指定只读属性:
interface Point {
readonly x: number;
y: number;
}
var p:Point = {x: 1, y: 2};
p.x++; // 报错,x为只读属性,不可以修改
p.y++; // 正确
上面接口属性的个数都是固定的,那么接口个数不固定的情况下要这么做呢,可以使用可索引的类型
与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型,比如a[10]
或ageMap["daniel"]
看个小例子:
// 数字索引相当于 声明一个字符串数组
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"]; // 数组里面只能是字符串,等价于 myArray = {0:"Bob",1:"Fred"}
let myStr: string = myArray[0];
这个例子里,我们定义了StringArray接口,它具有索引签名。
这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。
// 声明一个字符串索引
interface StringObj {
[index: string]: string;
}
let myObj: StringObj = {name:'张三','sex':'男'}; // 属性类型只能是字符串
let myObj: StringObj = {name:'张三','sex':'男',age:10}; // 报错,age是数字型,不被允许
配合readonly使用
interface ReadonlyStringArray {
readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error! 你不能设置myArray[2],因为索引签名是只读的。
TypeScript支持两种索引签名:字符串和数字,可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。 这是因为当使用 number
来索引时,JavaScript会将它转换成string
然后再去索引对象。 也就是说用 100
(一个number
)去索引等同于使用"100"
(一个string
)去索引,因此两者需要保持一致。
字符串类型
interface StringObjString {
[x: string]: string;
[y: number]: string;
name:string // name必需
}
let s1: StringObjString = { a: '1', 1:'d',name:'张三' }
数字类型
interface StringObjNumber {
[x: string]: number;
[y: number]: number;
name?: number // name可选
}
let s2: StringObjNumber = { a: 1, 4:1,c: 1 }
任意类型
interface StringObjAny {
[x: string]: any;
[y: number]: string;
name?:string
}
let s3: StringObjAny = { a: 1, 4:'d',c: true }
let Add: (x: number, y: number) => number;
interface Add{
(x:number,y:number):number
}
type Add = (x:number,y:number) => number
let add = (a,b) => a-b;
混合型接口
interface Blend{
(): void; // 什么都不返回
author:string;
setmethod():void;
}
function blend(){
// b:Blend = () => {}; // 报错
let b:Blend = (() => {}) as Blend; // √
b.author = 'shenlanse';
b.setmethod = () => {};
return b;
}
let b1 = blend();
b1.author;
b1.setmethod();
接口定义函数的一些知识点
可选参数
let add1 = (x:number, y:number, z?:number) => z ? x+y+z : x+y;
add1(1,2); // 3
add1(2,3,4); // 9
参数给默认值
let add2 = (x:number,y:number=9, z:number,e:number=3) => x+y+z+e;
add2(1,undefined,2); // 15
add2(2,3,4,5); // 14
剩余参数
let add3 = (x:number, ...result) => x + result.reduce((pre,cur)=>pre+cur)
add3(2,1,2,3,4); //等价于add3(2,...[1,2,3,4]); // 12
函数重载,如果两个函数名称相同,参数个数或者参数类型不同,就实现了参数重载
好处,不需要为了相似的功能的函数,使用不同的函数名称,从而增加函数的可读性
function add4(...result:number[]): number;
function add4(...result: string[]): string;
function add4(...result: Array): string;
function add4(...result:any[]):any{
const first = result[0];
if(typeof first === 'number'){
return result.reduce((pre,cur)=>pre+cur)
}
if(typeof first === 'string'){
return result.join('')
}
};
add4(1, 2, 3);
add4('1', 2, 3);
add4('1','f');