Skip to content

高级类型

class类

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

基本使用:

TypeScript
// 定义类
class Person {}
// 使用类
const p = new Person()

作为类型使用:

TypeScript
class Person {
  age: number
  gender = ''						 // 简化写法
  // gender: string = '男'   // 完整写法
}

构造函数

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

const p = new Person(20, '')

实例方法

TypeScript
class Point {
  private x = 1;
  private y = 2;

  scale(n: number) {
    this.x *= n;
    this.y *= n;
  }
}

const p = new Point()
p.scale(10)

类的继承

类的继承有两种方式:extends(继承父类)和implements(实现接口)。

TypeScript
class Animal {
  move() {
    console.log("走两步");
  }
}

class Dog extends Animal {
  name = "二哈";
  bark() {
    console.log("旺旺!");
  }
}

const d = new Dog();
d.move();
TypeScript
itnterface Singable {
  name: string
  sing(): void
}

// 子类实现父类接口意味着,Person子类中必须定义 Singable 父类中所有的方法和属性
class Person implements Singable {
  name: '小明'
  sing() {
    console.log('爱是不是不开口才珍贵~')
  }
}

类成员可见性

可见性修饰符有三个:

修饰符作用
public共有的
protected受保护的(自己或子类内部可以使用,外部实例无法调用)
private私有的(只在自己类内可以使用)

public

TypeScript
// public 是默认可见性,一般情况下可以省略不写
class Animal {
  public move() {
    console.log('走两步')
  }
}

protected

TypeScript
class Animal {
  protected move() {
    console.log('走一走')
  }
  run() {
    this.move()
  }
}

const a = new Animal()
// a.move() 	   // 父类实例方法不能调用

class Dog extends Animal {
  eat() {
    this.move()  // 子类的其他方法中,可以调用
  }
}
const d = new Dog()
// d.move()      // 类实例方法不能调用

pritive

TypeScript
class Animal {
  private run() {
    console.log('哈喽啊')
  }
  
  protected move() {
    this.run()      // 父类的 protected 方法中可以调用
  }
  
  run() {
    this.run()      // 父类的 public 方法中可以调用
  }
}

const a = new Animal()
// a.run() 		      // 父类的实例方法不能调用

class Dog extends Animal {
  eat() {
    a.run()         // 子类的普通方法中不能调用
  }
}
const d = new Dog()
// d.run()          // 子类的实例方法不能调用

交叉类型

交叉类型使用 & 关键字声明,功能类似于接口继承(extends),用于组合多个类型为一个类型

TypeScript
interface Person {
  name: string
}
interface Concat {
  phone: string
}

// 交叉类型
type PersonDetail = Person & Concat

let obj: PersonDetail = {
  name: '小明',
  phone: '123456'
}

交叉类型和继承对比

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

  • 不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。

    TypeScript
    interface A {
      fn: (value: string) => {}
    }
    
    // 继承
    interface B extends A { // 注意:此时的 B 会报错(不能将 string 类型分配给 number)
      fn: (value: number) => {}
    }
    TypeScript
    interface A {
      fn: (value: string) => {}
    }
    interface B {
      fn: (value: number) => {}
    }
    
    // 交叉类型
    type C = A & B
    
    // value 既可以是 string 类型,也可以是 number 类型
    let obj: C = {
      fn(value: string | number) {
        return ''
      }
    }

泛型

当想传递任意类型,还要类型检查,就可以使用泛型。

TypeScript
// 定义泛型函数
function getId<T>(value: T): T {
  return value
}

// 调用泛型函数
const num = getId<number>(123)
const str = getId<string>("admin")

泛型约束

泛型函数的类型变量 T 可以代表任意类型,这就导致无法访问某些属性。

TypeScript
function getId<T>(value: T): T {
  // 此时的 length 就会报错!因为不确定 Type 到底是什么类型,如 number 没有 length 属性
  const length = value.length
  return length
}

解决方案:

  • 指定更加具体的类型

  • 添加约束

TypeScript
// 指定为 T 类型的数组,因为数组有长度
function getId<T>(value: T[]): T[] {
  value.length
  return value
}
TypeScript
interface ILength { length: number }

// T类型必须满足 ILength 指定的要求
function getId<T extends ILength>(value: T): T {
  value.length
  return value
}

keyof获取泛型参数

泛型的类型变量可以有多个,并且类型变量之间还可以约束(比如,第二个类型变量受第一个类型变量约束)

keyof 关键字接受一个对象类型,生成其键名称的联合类型。

TypeScript
// keyof T 实际上获取的是 person 对象所有键的联合类型,也就是 'name' | 'age'
function getProp<T, key extends keyof T>(obj: T, key: key) {
  return obj[key]
}

class Person {
  name: "admin"
  age: 20
}
const person = new Person()

// 类型变量 key 受 Type 约束,即 key 只能是 Type 所有键中的任意一个,或者只能访问对象中存在的属性
const value1 = getProp(person, "name")
const value2 = getProp(person, "age")

泛型接口

在接口名称的后面添加 <类型变量>,这个接口就变成了泛型接口。

TypeScript
interface IdFunc<T> {
  id: (value: T) => T
  ids: () => T[]
}

let obj: IdFunc<number> = {
  id: (value) => value,  // id 方法的参数和返回值都是 number 类型
  ids: () => [1, 2, 3]   // ids 方法无参数,返回值是 number[]
}

泛型类

class也可以配合泛型来使用。

TypeScript
class GenericNumber<T> {
  defaultValue: T
  add: (x: T, y: T) => T
}

// 实例化的时候,明确指定实例的类型
const myNum = new GenericNumber<number>()
myNum.defaultValue = 10

泛型工具类型

TS 中内置了一些常用的工具类型,来简化 TS 中的一些操作。

Partial<T>

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

TypeScript
interface Props {
  id: number
  hobby: string[]
}

// 把 Props 接口中定义的属性全部变为可选
type partialProps = Partial<Props>

Readonly<T>

Readonly<T> 用来构造一个类型,将 T 的所有属性都设置为 只读

typescript
interface Props {
  id: number
  hobby: string[]
}

// 把 Props 接口中定义的属性变为只读
type partialProps = Readonly<Props>

Pink<T>

Pink<T> 可以从 T 中选择一组属性来构造新的类型

typescript
interface Props {
  id: number
  title: string
  hobby: string[]
}

// PickProps 就有了 Props 中 id、title 两个属性
type PickProps = Pick<Props, 'id' | 'title'>

Record<keys, T>

Record<keys, T> 构造一个对象类型,属性键为 keys,属性类型为 T。

typescript
type RecordObj = Record<'a' | 'b' | 'c', string[]>

// 上面代码等价于
type RecordObj = {
  a: string[]
  b: string[]
  c: string[]
}

let obj: RecordObj = {
  a: ['a'],
  b: ['b'],
  c: ['c']
}

索引签名类型

当我们无法确定对象中有哪些属性时,就可以使用索引签名类型了。

对象:

TypeScript
interface anyObject {
  // [key: string] 来约束该接口中,只要是 string 类型的属性名称,都可以出现在对象中。
  [key: string]: number
}

let obj: anyObject = {
  a: 1,
  b: 2
}

数组:

TypeScript
interface array<T> {
  [index: number]: T
}

let arr: array<number> = [1, 2, 3]
let arr1: array<string> = ['1', '2', '3']

映射类型

映射类型是基于旧类型,创建一个新类型(对象类型)。

映射类型是基于索引签名类型的,所以,该语法中也使用了索引签名类型 [] 的用法。

注意

映射类型只能在类型别名(Type)中使用,不能在接口(interface)中使用。

TypeScript
// 如果 PropKeys 的属性值很多时,一个一个定义就显得乏力
type Type1 = { 
  x: number; 
  y: number; 
  z: number;
}

type PropKeys = 'x' | 'y' | 'z'
// 无论 PropKeys 的属性值有多少个,都会自动生成
type Type2 = { [key in PropKeys]: number }

keyof

映射类型既可以根据联合类型创建新类型,也可以根据对象类型创建新类型。

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

// keyof Props 获取到对象类型 Props 中所有键的联合类型,即 'a' | 'b' | 'c'
// key in 表示 key 可以是 Props 中所有的键名称中的任意一个
// Props[key] 表示获取每个键对应的类型
type newProps = { 
  [key in keyof Props]: Props[key] 
}

索引查询类型

使用T[p]的方式在 TS 中实现根据索引查询类型。

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

type newProps = Props["a"]          // number
type newProps1 = Props["a" | "b"]   // number | string
// 查询全部
type newProps2 = Props[keyof Props] // number | string | boolean

Released under the MIT License.