文章目录
TS高级类型
class类
class Person {
age:number // 1.指定类型
gender = '男' // 2.设置默认值,自动指定类型
}
const p = new Person()
// p的类型为Person
构造函数
class Person {
age: number
gender: string
constructor(age: number,gender: string){
this.age = age
this.gender = gender
}
}
// 注意:JS可以直接使用构造函数constructor()
// TS中则需要先在类中规定属性的类型
// 即this.age对应Person类中的age: number
// age对应constructor中的age: number
实例方法
class Point{
x = 10
y = 20
scale(n: number): void{
this.x *= n
this.y *= n
}
}
实现接口
// 通过implements让class实现接口
// 实现接口意味着Person类中必须提供Singable接口指定的所有属性和方法
interface Singable{
sing(): void
}
class Person implements Singable{
sing(){
console.log('sing')
}
}
修饰符
- public(公有):默认,可以被任何地方访问
class Animal{
public move(){
console.log('123')
}
}
- protected(受保护):仅在对其声明所在类和子类中(非实例对象)可见
class Animal{
protected move(){
console.log('123')
}
}
class Dog extends Animal{
bark(){
console.log('456')
this.move()
}
}
const a = new Animal()
a.move() // 报错,实例对象中不可见
-
private(私有的):只在当前类中可见(实例对象和子类都不可见)
-
readonly(只读修饰符):防止在构造函数之外对属性进行赋值;只能修饰属性,不能修饰方法
class Person{
readonly age: number = 18 // 默认值
constructor(age: number){
this.age = age // 构造函数中赋值
}
}
// 其他地方都不能进行赋值
注意:readonly和const的意思一样,都是不能修改,所以它和const一样,如果直接赋初值(不指定类型),数据类型就会变成字面量
class Person{
readonly age = 18
constructor(age: number){
this.age = age // 报错,因为this.age数据类型为18
}
}
同样,readonly也可以在接口、对象等地方使用
类型兼容性
TS采用结构化类型系统(duck typing):类型检查关注的是值所具有的形状
对象之间的类型兼容
成员多的可以赋值给成员少的
class Point {x: number; y: number}
class Point3D {x: number; y: number; z: number}
const p: Point = new Point3D()
// p被设置为Point类型,却是Point3D的实例
// TS只检查二者结构相同(Point3D可以兼容Point),因此类型可以兼容
c#、Java中使用的是标明类型系统,不可以这样写
接口之间的类型兼容
同理,接口、函数也存在兼容性
interface Point {x: number; y:number}
interface Point3D {x: number; y:number; z: number}
let p1: Point
let p2: Point3D
P1 = P2
// 类和接口也是兼容的
class Point3D {x: number; y:number; z: number}
let p3: Point = new Point3D()
函数之间的类型兼容
函数之间的兼容性要考虑:参数个数、参数类型、返回值类型
与对象规则相反的原因主要是为了适应JS的函数写法
参数个数:参数多的兼容参数少的(与对象相反,参数少的可以赋值给多的)
// 因为在JS中,省略用不到的函数参数很常见,所以产生了参数少的可以赋值给参数多的这种形式
type F1 = (a: number) => void
type F2 = (a: number,b: number) => void
let f1: F1
let f2: F2 = f1
参数类型:相同位置的参数类型要相同或兼容(与对象相反,参数的参数类型少的可以赋值给多的)
interface Point2D {x: number; y:number}
interface Point3D {x: number; y:number; z: number}
type F2 = (p: Point2D) => void
type F3 = (p: Point3D) => void
let f2: F2
let f3: F3 = f2
返回值类型:函数返回值在JS没有特殊的写法,所以不需要和对象相反,如果返回的是对象,成员多的可以赋值给成员少的
type F5 = ()=>{name: string}
type F6 = ()=>{name: string; age: number}
let f5: F5
let f6: F6
f5 = f6
交叉类型
类似于接口继承extends,可以组合多个类型为一个类型
interface Name {name: string}
interface Age {age: number}
type PersonDetail = Person & Age
和接口继承的区别:接口继承对于同名属性的冲突会报错;而交叉类型对冲突的类型都可以接受
泛型
保证类型安全的前提下,让函数等与多种类型一起工作,实现复用
基本使用
应用场景:我们想让一个函数传入什么数据就返回什么数据(当然any很方便,但是无法保证安全)
// 声明泛型函数
function id<Type>(value: Type): Type {return value}
// 调用泛型函数
const num = id<number>(10)
const str = id<string>('mkbird')
// TS可以自动类型推论
const num = id(10) // 自动推断出10为number
- 语法:函数名后加<类型变量>,类型变量名称自拟
- 类型变量处理的是类型而不是值
- 调用时在<>中加入具体的数据类型
泛型约束
默认情况下泛型变量可以表示多种类型,这会导致无法访问任何属性
// 由于value可能为多种类型,所以无法访问string的length
function id<Type>(value: Type): Type {
console.log(value.length) //报错
return value
}
这时就需要进行泛型约束,主要有两种方式
- 指定更加具体的类型
// 数组一定有length属性,将value的Type类型改为数组,返回值的Type类型也改为数组
function id<Type>(value: Type[]): Type[] {
console.log(value.length)
return value
}
- 添加约束(extends)
此处extends代表的是约束,而不是继承;该约束表示,传入值的类型必须有length属性
interface Length {length: number}
function id<Type extends Length>(value: Type): Type {
console.log(value.length)
return value
}
泛型的类型变量可以有多个,且类型变量间也可以约束
// 创建一个函数获取对象中属性的值(对象中必须存在这个属性)
// keyof Type用于接收对象,生成其键的联合类型
// 本例中接收到Person的name和age,联合类型为name | age
function getProp<Type,Key extends keyof Type>(obj: Type, key: Key){
return obj[key]
}
let person = {name:'mkbird', age:18}
getProp(person,'name') //mkbird
// 还可以接收数据类型的方法
getProp(18,'toFixed')
泛型接口
接口也可以配合泛型使用
interface Func<Type>{
id: (value: Type) => Type
fun: () => Type[]
}
// 使用泛型接口时,必须指定泛型的类型
let obj: Func<number> = {
id(value) { return value}
fun() { return [1,2,3]}
}
泛型类
class名称后添加<类型变量>
class Number<NumType>{
defaultValue: NumType
add: (x: NumType,y: NumType) => NumType
}
const num = new Number<number>()
num.defaultValue = 10
// 调用时可以省略的情况
class Number<NumType>{
defaultValue: NumType
add: (x: NumType,y: NumType) => NumType
constructor(value: NumType){
this.defaultValue = value
}
}
const num = new Number(10)
泛型工具类型
用于简化TS泛型操作
Partial<Type>
将Type的所有属性设置为可选
interface Props {
id: sring
children: number[]
}
type PartialProps = Partial<Props>
// 构造出来的新类型结构和原来的相同,只是属性变为可选
Readonly<Type>
将type的所有属性设置为只读Pick<Type,keys>
从Type中选择一组属性来构造新类型
interface Props {
id: sring
name: string
children: number[]
}
type PickProps = Pick<Props,'id' | 'name'>
Record<Keys,Type>
构造一个对象类型,键为Keys,值为Type
type RecordObj = Record<'a' | 'b' | 'c', string[]>
// 相当于
//type RecordObj = {
// a: string[]
// b: string[]
// c: string[]
//}
let obj: RecordObj = {
a: ['a'],
b: ['b'],
c: ['c']
}
索引签名类型
当无法确定对象中有哪些属性,就要用索引签名类型
使用[key: string]
来约束接口中出现的实现名称,这样obj中可以出现任意多个属性
// JS中对象的键是string类型的
// key只是一个占位符,可以替换成其他
interface AnyObject{
[key: string]: number
}
let obj: AnyObject = {
a: 1,
b: 2
}
映射类型
基于旧类型创建新类型(对象类型)、提高开发效率
只能在类型别名中使用,不能在接口中使用
type PropKeys = 'x' | 'y' | 'z'
type Type1 = {[key in PropKeys]: number}
// 等价于
// type Type1 = {x: number;y: number;z: number}
基于对象生成
type Props = {a: number;b: string;c: boolean}
// 多一步keyof获取对象属性
// 将a,b,c都变成了number
type Type3 = {[key in keyof Props]: number}
索引查询类型
type Props = {a: number;b: string}
type TypeA = Props['a'] // 查询a为number类型
type Type = Props['a'|'b'] // number|string
type Type = Props[keyof Props] // number|string