男胖友de部落格

TypeScript Note

· nanpangyou

使用类型谓词做类型收窄

type Rect = {
 height: number;
 width: number;
}

type Circle = {
 point: [number,number];
 radius: number;
}

const f1 = (a: Rect |Cricle) => {
 if(isRect(a)){
  a // 这里a的类型就是Rect
 }else if(isCircle(a)){
  a // 这里a的类型就是Circle
 }
}

// 使用类型谓词做类型收窄 这里要使用function关键字,不能使用箭头函数
function isRect(x: Rect | Circle): x is Rect {
 return 'height' in x && 'width' in x;
}

function isCricle(x: Rect | Circle): x is Circle {
 return 'point' in x && 'radius' in x;
}

可辨别联合( kind )


// 可辨别联合 它们都有一个kind属性  kind也可以用其他任意key代替,如 type , tag , category 等
type Circle = { kind: 'circle', point: [number, number], radius: number };
type Square = { kind: 'square', x: number};
type Shape = Circle | Square;

const f1 = (a: Shape) =>{
 if(a.kind === 'circle'){
  a // 这里a的类型就是Circle
 }else if(a.kind === 'square'){
  a // 这里a的类型就是Square
 }
}

// 使用同名,可辨别的简单类型的key做区分

交叉类型

可以使用&来实现交叉类型


type A = {
 name: string;
}

type B = {
 age: number;
}

type C = A & B;


const c:C = {
 name: 'xxx',
 age: 18
}

如果 A、B 无交集,则可能得到 never 也可能是属性 never

记录一个犯的错


type A = {name: string} // 代表对象拥有一个属性为name,并且该属性的值为string,(并不是只能有一个name属性)

type B = {age: number} // 不是指对象只能有一个属性age,而是指对象拥有一个属性为age,并且该属性的值为number 

type C = A | B

const d = {name : 'xxx', gender: 'male'}

const c: C = d //这样赋值是不会报错的

const cC = {name: 'xxx', gender: 'male'}  //这样赋值会报错

// ts 只在声明同时赋值的时候会严格检查类型,而在赋值的时候不会严格检查类型

类型兼容

小类型可以复制给大类型


// 简单类型
type A = string | number;
const a: A = 'hi'


// 简单对象
type Person = { name: string; age: number; }

const student = { name: 'Tom', age: 18, grade: 3 };
const aa: Person = student; // ok

// 获取一个没有声明对象类型的类型
const user = { name: 'Tom', age: 18, grade: 3 };
type User = typeof user;

指定this


type Person = { name: string }

function f(this: Person, word: string){
  console.log(this.name + word)
}

// 1. 拼凑 person.f()
const person: Person & {F: typeof f} = { name: 'xxx', F: f }
person.f('hi')

// 2. call
f.call(person, 'hi')

// 3. apply
f.apply(person, ['hi'])

// 4. bind
const f1 = f.bind(person)
f1('hi')

// =====================
f.bind(person)('hi')

// =====================
const f1 = f.bind(person)  // this => person
const f2 = f1.bind(null, 'hi') // word => 'hi'
f2()

as const

在开发过程中,有时需要ts将类型自动推导变小


const a = [1,3,4]
// 这里的 a 的类型可以是 number[] 也可以是 readonly [1,2,3], 逻辑都是可以说过去的
// 一般没有明确说明时,a 的类型是 number[]
const b = [1,3,4] as const
// 这里的 b 的类型就是 readonly [1,2,3],这样就可以保证 b 的值不会被修改
// 如果使用了 as const 那么 b.push(5) 就会报错, 因为 b 的类型是 readonly [1,2,3]
// 如果不使用 as const 那么 b.push(5) 就不会报错, 因为 b 的类型是 number[]




// 使用场景

function f(a: number, b: number, c: number){
  console.log(a + b + c)
}
const a = [1,2,3]
f(...a) // 由于 a 的类型是 number[],所以这里会报错,因为 f 的参数类型是 number, number, number,不能保证数组内容不可变

const a = [1,2,3] as const
f(...a) // 这里就不会报错了,因为 a 的类型是 readonly [1,2,3],这样就可以保证数组内容不可变

函数参数析构


type Config = {
  url: string
  method: 'get' | 'post'
  data?: unknown
  params?: unknown
}


// 普通析构
const ajax = ({ url, method }: Config) => {}

// 剩余参数析构
const ajax = ({ url, method, ...rest}: Config) => {}

// 默认值
const ajax = ({ url, method, ...rest}: Config = { url: '', method: 'get' }) => {}

// 默认值的另一种写法
const ajax = ({ url, method, ...rest} = { url: '', method: 'get' } as Config) => {}

索引签名 (Index Signature) 和 映射类型 (Mapped Types)


## 索引签名

type Person = {
  [k: string]: string
  length: number
}

type Person2 = {
  [k: number]: string
  length: number
}

## 映射类型 (常用于范型)

type Person = {
  [k in string]: string
  length: number
}

type Person2 = {
  [k in number]: string
  length: number
}