TypeScript高级类型(二)

34 篇文章 1 订阅
6 篇文章 0 订阅

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会飞的战斗鸡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值