ts的核心原则之一是对值具有的结构进行类型检测,接口的作用就是为这些类型命名和你的代码或第三方代码定义契约
// 4.1 接口的定义
interface User {
name: string,
age: number,
gender: boolean
}
// 函数的参数要求符合User接口的数据结构
function printUser(user: User) {
console.log(user.name + ' ' + user.age)
}
printUser({ age: 12, name: '海绵宝宝', gender: true })//海绵宝宝 12
// 接口的类型检测不会去检查属性的顺序
// 只要相应的属性存在且类型也是对的就可以了
// 属性不能多也不能少
// 4.2 接口写可选属性
// 接口的属性不是全部必须情况时
interface User {
name: string,
age: number,
gender?: boolean
}
function printUser(user: User) {
console.log(user.name + ' ' + user.age)
}
printUser({ age: 12, name: '海绵宝宝'})
// 4.3 任意属性
interface User {
name: string,
age: number,
// 任意属性
[propName: string]: any
}
function printUser(user: User) {
console.log(user.name + ' ' + user.age)
}
printUser({ age: 12, name: '海绵宝宝', gender: 2, hh: 3 })//后面可传别的任意值
// 4.4 只读属性
// 如果接口的数据结构中某个属性不允许赋值
interface User {
readonly name: string,
age: number
}
function printUser(user: User) {
user.name = '派大星' //能跑,但是会报错,因为name是 readonly 只读属性
console.log(user.name + ' ' + user.age)
}
// 5 泛型
// 书写一个函数,传入的参数是什么类型,要求返回的值又是什么类型
// 我们需要一种方法可以记录传入的参数类型
// 我们可以使用 类型变量 来记录 他是一种特殊的变量
// 5.1 泛型的普通用法
// function fn(arg: number): number { // 这样的话只能定义成传数字的函数了 定义的fn太死了
// return arg
// }
// function fn(arg: any): any { // 这样的话只能定义成传数字的函数了 定义的fn太活了和没定义一样
// return arg
//}
function fn<T>(arg: T): T {
return arg
}
// // 调用方式1
console.log(fn('hello'))
// // 调用方式2
console.log(fn<number>(16))
// 5.2 多个类型参数
function fn<T, S, F>(a: T, b: S, c: F): F {
return c
}
console.log(fn(1, '2', true))
console.log(fn<number, string, boolean>(1, '2', true))
// 5.3 泛型约束
// 我们有时候操作某一个类型的数值
// 并且我们知道这组数据具有什么样的属性
// 比如 我想访问arg的length属性
// 创建一个具有length属性的接口
interface User {
length: string,
Prototype: number
}
// 定义泛型T必须和我的接口一样,那么我就需要继承我的接口 用extends关键字
function fn<T extends User>(a: T): T {
console.log(a.length)
console.log(a.Prototype)
return a
}