[[toc]]
demo:描述普通对象:类型检查
使用interface检查属性类型
interface Person { name: string} // interface定义
function greeter(p: Person){return p.name} // interface使用
// 1. 隐式类型检查: 检查属性是否存在和匹配,不检查多余属性,不检查顺序
let user1 = {age:20, name: 'san'}; // ✅
console.log("user1: ", greeter(user1)); // user1: san
// 2. 显式类型检查: 检查属性是否存在和匹配,不能有多余属性,不检查顺序
let user2: Person = {name: 'san'}; // ✅
console.log("user2: ", greeter(user2)); // user2: san
// let user3:Person = {age:20,name: 'san'}; // ❌ error 'age' does not exist in type 'Person'
// 3. 类型推断 相当于隐式类型检查
let user4 = {age:20,name: 'san'} as Person; // ✅
console.log("user4: ", greeter(user4)); // user4: san
属性检查分类
- 隐式类型检查
- 显式类型检查
类型检查点
- 属性是否存在?
- 属性类型是否正确?
- 属性顺序是否可以随意?
- 属性是否可以多余?
demo:描述普通对象:可选属性
可选属性
- 属性名称后接问号
interface SquareConfig { color?: string, width?: number }
let sc1: SquareConfig = {} // ✅
let sc2: SquareConfig = {color: 'red'} // ✅
let sc3: SquareConfig = {width: 1} // ✅
let sc4: SquareConfig = {color: 'red', width: 1}// ✅
let sc5 = {color: 'red', opactiy: 1} as SquareConfig // ✅
let sc6: SquareConfig = {colr: 'red', width: 0.5} // ❌ 'colr' does not exist in type 'SquareConfig'
demo:描述普通对象:只读属性
只读属性
- 通过readonly, ReadonlyArray定义
- 初始化之后,只能读不能写
// 1. 只读属性 readonly
interface Point { readonly x: number; readonly y: number }
let p1: Point = {x: 1, y:2}
console.log(p1.x, p1.y); //✅ 1 2
p1.x = 3; // ❌ Cannot assign to 'x' because it is a read-only property.
// 2. 只读数组 ReadonlyArray
let intArr2: ReadonlyArray<number> = [4,5,6];
console.log(intArr2); // ✅ [ 4, 5, 6 ]
intArr2[0] = 400; // ❌ Index signature in type 'readonly number[]' only permits reading.
demo:描述函数:什么是函数接口?
- 描述函数类型,使用接口来描述函数
- 语法: 对象中仅包含一个函数,没有别的属性
- 用途: 可以定义多个此结构的函数
// 定义函数接口: 通过接口定义函数类型(参数+返回值)
interface searchFun {(source: string, subString: string) : boolean}
// 使用函数接口定义fun1
let fun1: searchFun = function(src: string, sub:string): boolean{
return src.search(sub) > -1;
}
// 使用函数接口定义fun2
let fun2: searchFun = function(src: string, sub:string): boolean{
return src.indexOf(sub) > -1;
}
console.log(fun1('abc123abc', '123'),fun1('abc123abc', '1234')); // true false
console.log(fun2('abc123abc', '123'),fun2('abc123abc', '1234')); // true false
demo:描述函数:对象接口和函数接口区别?
区别(先看例子)
- 语法: 对象接口里所有函数都有函数名; 函数接口里有个没有函数名的函数定义
- 作用: 对象接口描述的是对象(里面包含的属性和方法挂在对象上); 函数接口描述的是函数(里面包含的属性和方法挂在函数上)
// 1. 对象接口
// Schedule描述的是对象❗
interface Schedule {
start(): void; // 有方法名 作为对象的一个属性
time: string; // 属性
end(): void; // 作为对象的一个属性
}
// 使用对象接口
const s1:Schedule = {
start: function(){console.log('start')},
time: '20240101',
end: function(){console.log('end')},
}
// 运行 (打印时间、开始调度、结束调度)
console.log('对象接口')
console.log(s1.time); // 20240101
s1.start(); // start
s1.end(); // end
// 2. 函数接口
// Schedule描述的是函数❗
interface Schedule {
(): void; // 没有方法名 不是对象的一个属性
start(): void; // 有方法名 作为对象的一个属性
time: string; // 属性
end(): void; // 作为对象的一个属性
}
// 使用函数接口
const genSchedule = function(time:string):Schedule{
let main = <Schedule>function(){}
main.time = time;
main.start = function(){console.log('start')};
main.end = function(){console.log('end')};
return main;
}
// 运行 (打印时间、开始调度、结束调度)
console.log('函数接口')
const s1 = genSchedule('20240101'); // 生成s1函数
console.log(s1.time); // 20240101
s1.start(); // start
s1.end(); // end
demo:描述函数:混合类型接口
接口内包含(先看例子)
- 函数: 类似函数接口
- 属性: 挂在函数对象上
- 方法: 挂在函数对象上
// 定义混合接口
interface Lib {
(): void; // 没有方法名 不是对象的一个属性,Lib接口描述的是函数❗
version: string; // 属性
doSomething(): void; // 方法
}
// 使用混合接口
function genLib (version: string): Lib {
const lib = <Lib> function() {} // 相当于 let lib = (() => { }) as Lib;
lib.version = version || "1.0";
lib.doSomething = () => {console.log("do something")};
return lib;
}
// 运行
let lib = genLib('2.0') // 返回函数 lib
console.log(lib.version) // lib属性version => 2.0
lib.doSomething(); // lib方法doSomething => do something
// 需求: 定义一个函数,实现变量累加
// 方法1 js 全局变量
// 缺点: count 会污染全局空间
let count = 0;
function demo() {
console.log(++count);
}
demo(); // 1
demo(); // 2
demo(); // 3
// 方式二(使用闭包)
// 缺点: 闭包容易使用不当导致内存泄露
let demo = (() => {
let count = 0;
return () => {
console.log(++count);
}
})();
demo(); // 1
demo(); // 2
demo(); // 3
// 方式三(利用 JS 中函数的本质)
// 思路: JS 中函数本质就是对象,可以直接给该对象添加 count 属性进行累加
function demo() {
console.log(++demo.count);
}
demo.count=0
demo(); // 1
demo(); // 2
demo(); // 3
// 方式三 TS 版本
interface Counter {
():void; // 函数结构, 无参数,无返回值
count: number; // 函数带有count属性
}
// 自执行函数: 匿名函数,参数为空,此处 Counter 作为函数返回值类型
const demo = (function(): Counter{
// fun 作为函数时符合接口中函数接口的限定 ():void
// fun 作为对象时符合接口中对象属性的限定 count:number
// 此处 Counter 是类型断言语法,相当于 as Counter
const fun = <Counter> function() {console.log(++fun.count)}
// const fun = function() {console.log(++fun.count)} as Counter;
fun.count = 0;
return fun;
})()
demo(); // 1
demo(); // 2
demo(); // 3
demo:描述索引:索引签名 Index Signatures
索引签名是什么?
- 就是指索引下标: objec.key或者array.index
- 支持两种索引签名: string,number
- 注意: 索引签名number和string类型同时存在时,2个的返回值类型需要相同。这是因为当使用 number来索引时,将它转换成string然后再去索引对象。 比如100作为索引等同于"100"作为索引,因此两者返回值类型需要保持一致。
// 1. 索引签名为number
interface Grades {[x: number]: number};
let myGrade: Grades = [90,95,100]; // ✅
let grade0: number = myGrade[0]; // ✅
console.log(`grade0=${grade0}`); // grade0=90
// 2. 索引签名为string
interface NameGrades {[y: string]: number};
let myNameGrades: NameGrades = {'lily': 80, 'Jonny': 100}; // ✅
let lucyGrades: number = myNameGrades['Jonny'];
console.log(`lucyGrades=${lucyGrades}`); // lucyGrades=100
// 3. 索引签名为number+string
interface Okay { [x: number]: string; [x: string]: string;}
let myOkay: Okay = {'name': 'micky', 10: 'haha'};
let okRet1: string = myOkay['name']; // ✅ micky
let okRet2: string = myOkay[10]; // ✅ haha
// 4. 索引签名包含index+length
// ok length属于字符串索引
interface nameDictionary { [index: number]: number, length: number}
let myNameDictionary: nameDictionary = [1,2,3]; // ✅
// 5. 索引签名可以设置为只读
interface ReadonlyStringArray {readonly [index: number]: string;}
let myReadonlyStringArray: ReadonlyStringArray = ['Alice', 'Bob'];
console.log(myReadonlyStringArray[0]); // ✅ 只能读 Alice
// myReadonlyStringArray[0]='Angel' // ❌ 不能写
// 注意:如果2个的返回值类型不同时,编译报错
// ❌ Numeric index type 'number' is not assignable to string index type 'string'.
interface NotOkay { [x: number]: number; [x: string]: string;}
总结
接口是什么?
- 用户自定义的类型,定义基本类型中不存在类型
- 个人理解:相当于c++的struct, js中的object
接口作用
- 描述普通对象: 约定对象的kv类型
- 描述函数: 约定函数输入和输出类型
- 描述索引: 约定object/array的索引类型