高级类型

class 类

🟢 TS 中的 class 不仅仅提供 class 语法功能,也作为一种类型存在

🟠 实例属性初始化

class Person {
  age: number
  gender: '男'
}

🟠 构造函数

class Person {
  age: number
  gender: string
  constructor(age: number, gender: string) {
    this.age = age
    this.gender = gender
  }
}

注意

成员初始化后才能通过 this. 访问实例成员

需要为构造函数指定类型注解,否则会推断为 any 构造函数不需要返回值

🔵 实例方法

class Point {
  x = 1
  y = 2
  scale(n: number): void {
    this.x *= n
    this.y *= n
  }
}

🟣 继承

实现继承:extends (继承父类) impelments (实现接口)

说明

JS 中只有 extends , impelments 是由 TS 提供

🟡 继承父类

class Animal {
  move() {
    console.log('moving along !')
  }
}
class Dog extends Animal {
  bark() {
    console.log('wang wang wang !!')
  }
}

const dog = new Dog()
dog.move()
dog.bark()





 








🟡 实现接口

interface Singable {
  sing(): void
}

class Man implements Singable {
  sing(): void {
    console.log('hhhhhhhh!!')
  }
}

🟠 可见性修饰符

1️⃣ public 共有成员可以被任何地方访问,public 是默认的可以省略

class Animal {
  public move() {
    console.log('moving along !')
  }
}

2️⃣ protected 受保护的,仅对声明所在类和子类可见 子类方法内部通过 this访问

class Animal {
  // 受保护的方法
  protected move() {
    console.log('走!')
  }
  run() {
    this.move()
    console.log('跑!')
  }
}

const a = new Animal()
a.run()
// a.move() 不可访问

class Dog extends Animal {
  bark() {
    this.move()
    console.log('狗叫!')
  }
}

const d = new Dog()
d.run()
d.bark()
// d.move() 不可访问

3️⃣ private 私有的,只在当前类可见,实例对象以及子类不可见

class Animal {
  private __move__() {
    console.log('private 方法')
  }

  move() {
    this.__move__() //可以访问
    console.log('走!')
  }
  run() {
    console.log('跑!')
  }
}

const a = new Animal()
// a.__move__() 私有,不可在外部访问

class Dog extends Animal {
  bark() {
    // this.__move__() 私有,不可在外部访问
    console.log('狗叫!')
  }
}

const d = new Dog()
// d.__move__() 私有,不可在外部访问

4️⃣ readonly 只读 防止在构造函数之外对属性进行赋值

class Person {
  readonly age: number = 18
  constructor(age: number) {
    this.age = age
  }
  setAge(age: number) {
    // this.age = age age为只读属性
  }
}

说明

readonly 修饰的属性,必须手动提供明确的类型

类型兼容

类型系统有:

🟢 Structural Type System (结构化类型系统)

🟢 Nominal Type System (标明类型系统)

TS 采用的是结构化类型系统,类型检查关注的是值所具有的形状

在结构系统中,如果两个对象具有相同的形状,则认为是同一类型

成员多的可以赋值给少的

class interface 之间也可以兼容

class Point {
  x: number
  y: number
}

class Point2D {
  x: number
  y: number
}
const p: Point = new Point2D()

🟥 函数之间的兼容性

函数兼容性要考虑:参数个数、参数类型、返回值类型

1️⃣ 参数个数

参数少的可以赋值给参数多的

type F1 = (a: number) => void
type F2 = (a: number, b: number) => void
let f1: F1 = function () {}
let f2: F2 = f1

2️⃣ 参数类型

相同位置的参数类型要相同或兼容

// 原始类型 类型要保持一致
type F1 = (a: number) => String
type F2 = (a: number) => String
let f1: F1 = function () {
  return 'ok'
}
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 = function () {}
let f3: F3 = f2

3️⃣ 返回值类型

多的可以给少的

总结

对象-多赋值少

函数参数个数-少赋值多

函数参数类型-原始类型必须相同,对象类型把对象拆开看成参数,少的可以赋值多的

函数返回值-多赋值少

交叉类型

使用 &

类似于接口继承,用于组合多个类型为一个类型

interface Person {
  name: string
}
interface Contcat {
  phone: string
}

type PersonDetail = Person & Contcat

let obj: PersonDetail = {
  name: 'tom',
  phone: '110'
}

& VS extends 对比

相同点:都可以实现对象类型的组合

不同:对于同名属性之间处理类型冲突不同

接口继承

// 类型不兼容
// error
interface A {
  age: number
}
interface B extends A {
  age: string
}




 



交叉类型

interface A {
  age: number
}
interface B {
  age: string
}
type C = A & B

// 等价于
age: string | number

泛型和 keyof

创建泛型函数

function id<type>(value: type): type {
  return value
}

const num = id<number>(10)

let str = id('hello')

说明

在函数名称后面加<>,在里面添加类型变量<T>

类型变量是特殊类型的变量

泛型约束

不添加约束无法访问值的任何属性

添加约束

interface ILength {
  length: number
}

function id<T extends ILength>(value: T): T {
  value.length
  return value
}
// let num = id(10)

let str = id('hello')








 


keyof

例子:创建一个函数获取对象中属性的值

function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}
let person = { name: 'tom', age: 18 }
getProp(person, 'age')

说明

keyof 接收一个对象类型,生成键名称的联合类型

本例中 K 受 T 约束,K 只能是 T 所有键中的任意一个

泛型接口

interface IdFunc<T> {
  id: (value: T) => T
  ids: () => T[]
}
let obj: IdFunc<string> = {
  id(value) {
    return value
  },
  ids() {
    return ['a']
  }
}

说明

接口的类型变量对接口中所有成员可见

使用泛型接口时需要显示指定类型

泛型工具类型

🟠 Partial<Type> 用来构造一个类型,将 Type 的所有属性设置为可选

interface Props {
  id: string
  children: number[]
}

type PartialProps = Partial<Props>

// 所有属性都为必填
let MyProps: Props = {
  id: 'aaa',
  children: [1, 2]
}

// 所有属性变为可选
let MyPartialProps: PartialProps = {
  id: 'aa'
}

🟠 Readonly<Type> 构造一个类型,将 Type 所有属性设置为只读

interface People {
  name: string
  age: number
}

type ReadonlyPeople = Readonly<People>

let Tom: ReadonlyPeople = {
  name: 'Tom',
  age: 20
}

// 无法分配到 "age" ,因为它是只读属性
// Tom.age = 21

🟠 Pick<Type, Keys> 从 Type 里面选择一组属性来构造新类型

interface Text {
  id: number
  title: string
  author: string
}

type PickText = Pick<Text, 'id' | 'author'>

let MyPickText: PickText = {
  id: 1,
  author: 'tom'
}

说明

Type: 选择谁的属性

Keys: 选择那些属性

第二个类型变量传入的属性只能是第一个类型变量存在的属性

🟠 Record<Keys,Type> 构造一个对象类型,属性键为Keys 属性类型为Type

type RecordObj = Record<'id' | 'name' | 'age', string[]>

let Obj: RecordObj = {
  id: ['1'],
  name: ['1'],
  age: ['1']
}

说明

创建的类型里的属性类型都是一致的

索引签名类型 和 索引查询类型

索引签名类型

使用场景:无法确定对象中有哪些属性

interface AnyObj {
  [key: string]: number | string
}

let Obj: AnyObj = {
  1: 1,
  aaa: '111'
}

说明

使用 [key: string] 约束接口中允许出现的属性名,表示只要是string类型的都可以

可以出现任意多个

索引查询类型

作用:查询属性的类型

type Props = {
  a: number
  b: string
  c: boolean
}

type PropsA = Props['a']

type PropsAB = Props['a' | 'b']

type PropsAll = Props[keyof Props]

映射类型

Partial<Type> 实现原理

type Partial<T> = {
  [P in keyof T]?: T[P]
}

interface Props {
  id: string
  children: number[]
}

type PartialProps = Partial<Props>

说明

keyof T => keyof Props 表示获取 Props 所有的键 'id'|'children'

[]?,表示变为可选属性

T[P] 表示获取 T 中每个键对应的类型

贡献者: SeeGreatHu