你的 TypeScript 还只是用来声明 string、number……的吗?

简介: 使用|操作符将变量可能出现的数值类型连接起来,就是联合类型

深入TS类型


联合类型

使用|操作符将变量可能出现的数值类型连接起来,就是联合类型

function css(ele: Element, attr: string, value: string | number) {
  // value参数的类型只能是string或者number
  // ......
}
复制代码

交叉类型

使用&操作符将多种类型合并在一起,形成一种新的类型

type t1 = {
  name: string,
  age: number
}
type t2 = {
  sex: string
}
type t3 = t1 & t2
function sayHi(person: t3) {
  console.log(`${person.name}今年${person.age}岁,性别:${person.sex}`)
}
let king = {
  name: 'king',
  age: 12,
  sex: 'man'
}
let ali = {
  name: 'king',
  sex: 'woman'
}
sayHi(king)
sayHi(ali) // 报错
复制代码

这里t1和t2两个类型通过交叉合并为一种类型,要求必须含有三个属性并且符合数据类型限定,上面的代码中最后一行会抛出错误,因为变量ali不满足t3类型的要求

1682562586(1).png

这里需要说一下interface和type,都可以描述一个对象或者函数,interface用来定义接口,type定义类型,二者并不是互相独立的。

这里说一下二者的区别:

  • type可以直接通过运算获得新类型,而interface需要使用extends操作符来生成新的接口。
  • type 语句中还可以使用 typeof 获取实例的类型进行赋值
  • interface 能够声明合并,多个同名interface声明的类型会被合并到同一接口

1682562609(1).png

从图中可以看到interface也可以来继承type,并且实现相同功能


字面量类型


TS类型限定除了基本数据类型限定之外还可以进行具体值的限定

type t1 = {
  name: string,
  sex: 'man' | 'woman'
}
let person: t1 = {
  name: '张三',
  sex: 'middle' // 报错
}
复制代码

1682562638(1).png

如上,使用字符串限定之后sex的值之能是man或者woman。除了字符串以外,其他的基本数据类型也都可以限制


类型别名


可以使用type操作符对任意类型获取组合类型起别名

1682562659(1).png


类型推导


每次都显式标注类型太麻烦,TS提供了一种方便的特性——类型推导。根据上下文自动推导出对应的类型标注,过程发生在:

  • 初始化变量
  • 设置函数默认参数
  • 返回函数值

1682562681(1).png


类型断言


有的时候我们需要一个更精确的标注,比如使用document.querySelector方法获得的结果,TS会根据参数判断返回值的类型。如果是一个图片,他有src属性,而如果是一个div,他就没有src属性。

1682562697(1).png

1682562716(1).png

但是TS并不会知道我们使用了类选择器之后的结果,我们需要提前告知

let ele = <HTMLImageElement>document.querySelector('.box')
// 或者
let ele2 = document.querySelector('.box') as HTMLImageElement
复制代码

断言也可以联合类型参数带来的问题吗,如果参数是联合类型,比如string | string[],参数有可能是数组也有可能是字符串,那么无法判断参数是否具有某一方法,这时候就可以使用类型断言来调用方法


类型保护


上面说的联合类型参数带来的问题也可已通过类型保护来解决

function fun(str: string | string[]) {
  if (typeof str === 'string') {
    str.split('')
  } else {
    // 当其他类型都在if中罗列之后,剩余的一种在else中生效
    str.push()
  }
}
复制代码

intanceof也可以触发类型保护


类型操作符


keyof

可以获取类型的键名组成的联合类型,主要用途是key的合法化

type Person = {
  name: string;
  age: number;
}
type PersonKey = keyof Person;
// PersonKey得到的类型为 'name' | 'age'
function getValue(p: Person, k: keyof Person) {
  return p[k];
}
复制代码

typeof

获取一个对象/实例的类型,只能用在具体的对象上

1682562745(1).png

in

只能用在类型的定义中,可以对枚举类型进行遍历

// 这个类型可以将任何类型的键值转化成number类型
type TypeToNumber<T> = {
  [key in keyof T]: number
}
复制代码


泛型


泛型在之前的入门中也有所涉及,但那只是皮毛中的皮毛,现在我们来深入一下泛型,基本的泛型定义不再赘述,可以去看一下TypeScript的入门笔记

泛型推导

如同前面所说的类型推导一样,泛型也有推导,

type Dog<T> = {
  name: string,
  type: T
}
function doSth<T>(dog: Dog<T>) {
  return dog.type
}
let dog = {
  name: 'lucky',
  type: 'pug'
}
// 如果没有泛型推导,就需要写这么多
// let dog: Dog<string> = {
//   name: 'lucky',
//   type: 'pug'
// }
doSth(dog)  // 调用函数时根据type的类型自动推导出了T
复制代码

类型约束

有时候,我们需要限定类型,这时候使用extends关键字

function sum<T extends number>(value: T[]) {
  let count = 0
  value.forEach(v => count += v)
  return count
}
复制代码

使用extends限定参数类型为数字数组,如果调用时参数出现了其他类型就会报错

1682562771(1).png

还可以限制多个参数,如

function pick<T, U extends keyof T>(obj: T, proto: U) {
  console.log(obj[proto]);
};
let person = {
  name: '张三',
  age: 14
}
pick(person, 'name')
复制代码

第二个参数必须是第一个参数中keys的子集,否则报错

1682562792(1).png


泛型推断


使用infer关键字来进行泛型推断

type Per<T> = T extends { t: infer P } ? P : string
type p = Per<number> // string
type p1 = Per<{ t: boolean }> // boolean
type p2 = Per<{ t: number }>  // number
复制代码

定义Per的过程也叫泛型条件,通过三目运算符控制最终分配的类型。根据传进来的泛型类型T有无t属性来确定返回的类型,并将t的属性返回


常用泛型工具


Partial

Partial的作用是将传入的属性变成可选项,原理就是使用keyof拿到所有属性名,然后再使用in遍历,使用?将属性设置为可选属性,其实现源码如下

type Partial<T> = {
  [P in keyof T]?: T[p]
}
type person = {
  name: string,
  age: number
}
// 使用Partial处理后没有全部属性不再报错
let a: Partial<person> = {
  name: '1q23'
}
复制代码

Required

Required将所有的参数变为必选项;其原理是使用keyof拿到所有属性名,然后再使用in遍历,可以理解为使用-?抵消掉?

type Required<T> = {
  [P in keyof T]-?: T[P]
}
type person = {
  name: string,
  age: number
}
let b: Required<person> = {
  name: 'king',
}
复制代码

Record

Record作用是用于构造键为K类型值为T类型的对象,源码也比较简单,将K中的每个属性设置为T类型

type Record<K extends keyof any,T> = {
  [key in K]: T
}
// keyof any对应的类型为number | string | symbol,就是可以做索引的类型集合
type person = {
  name: string,
  age: number
}
let c: Record<number, string> = {
  1: 1,
  'str': 123
}
复制代码

这里会抛出异常,因为使用Record工具将所有的number类型为索引的属性值类型设置为string,数字索引的属性不是string就会报错

1682562823(1).png

Pick

Pick的作用是将 T 类型中的 K 键列表提取出来,生成新的子键值对类型

type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}
type person = {
  name: string,
  age: number
}
let d: Pick<person, 'name'> = {
  name: 'king',
  age: 12 
  // 不能将类型“{ name: string; age: number; }”分配给类型“Pick<person, "name">”。对象文字可以只指定已知属性,并且“age”不在类型“Pick<person, "name">”中
}
复制代码

因为使用Pick工具从person类型中只选择了name属性,所以添加age属性之后会报错

Exclude

Exclude的作用是去除 T 类型和 U 类型的交集,返回剩余的部分

type Exclude<T, U> = T extends U ? never : T
type person = {
  name: string,
  age: number
}
type el = Exclude<person | string, number | 'a' | person> // string
let e: el = 'str'
复制代码

Omit

Omit用于键值对对象的 Exclude,它会去除类型 T 中包含 K 的键值对

type Omit = Pick<T, Exclude<keyof T, K>>
type person = {
  name: string,
  age: number
}
type oi = Omit<person, 'age'>
let f: oi = {
  name: 'king'
}
复制代码

ReturnType

ReturnType获取 T 类型(函数)对应的返回值类型,通过使用 infer 推断返回值类型,然后返回此类型

type ReturnType<T extends (...args: any) => any>
  = T extends (...args: any) => infer R ? R : any;
type T0 = ReturnType<() => string>; // string
type T1 = ReturnType<(s: string) => void>; // void


相关文章
|
6月前
|
存储 JavaScript 安全
TypeScript 中的 Number 类型,Number 类型的特性、常见操作和注意事项
TypeScript 中的 Number 类型,Number 类型的特性、常见操作和注意事项
469 1
|
4月前
|
JavaScript 前端开发 开发工具
TypeScript的介绍,let age:number = xxx,可以直接看出数据类型,Type由微软开发,可以在任何浏览器和系统中运行,比较适合大型项目,TypeScript的安装
TypeScript的介绍,let age:number = xxx,可以直接看出数据类型,Type由微软开发,可以在任何浏览器和系统中运行,比较适合大型项目,TypeScript的安装
|
5月前
|
JavaScript 前端开发 索引
JavaScript有7个数据类型:Number, String, Boolean, Null, Undefined, Symbol(BES6)和BigInt(ES10)组成基本类型
【6月更文挑战第25天】JavaScript有7个数据类型:Number, String, Boolean, Null, Undefined, Symbol(BES6)和BigInt(ES10)组成基本类型,而Object包括Array、Function等是引用类型。Objects可以包含键值对,Array是特殊的Object。Functions也是对象。`null`和`undefined`被视为特殊的原始值。
54 1
TS定义布尔值,let flag:boolean = true,定义数字类型 let a1:number = 10,赋值 let str1:string = ‘‘,打印c~.log($(str1))
TS定义布尔值,let flag:boolean = true,定义数字类型 let a1:number = 10,赋值 let str1:string = ‘‘,打印c~.log($(str1))
TS,数据类型概述,常见的基本数据类型有number/string/boolean/undefined/null,字符串用““,let food: string = ‘糖葫芦‘,布尔类型
TS,数据类型概述,常见的基本数据类型有number/string/boolean/undefined/null,字符串用““,let food: string = ‘糖葫芦‘,布尔类型
|
6月前
|
JavaScript 前端开发
TypeScript内置类型一览(Record<string,any>等等)(下)
TypeScript内置类型一览(Record<string,any>等等)
|
6月前
|
JavaScript
TypeScript内置类型一览(Record<string,any>等等)(中)
TypeScript内置类型一览(Record<string,any>等等)
|
6月前
|
JavaScript
TypeScript内置类型一览(Record<string,any>等等)(上)
TypeScript内置类型一览(Record<string,any>等等)
124 0
|
6月前
|
JavaScript 前端开发
js基础语法:包括变量声明、数据类型(Number, String, Boolean, Null, Undefined, Symbol, Object)、运算符、流程控制语句(if...else, switch, for, while, do...while)等。具体案例使用演示
js基础语法:包括变量声明、数据类型(Number, String, Boolean, Null, Undefined, Symbol, Object)、运算符、流程控制语句(if...else, switch, for, while, do...while)等。具体案例使用演示
78 1
|
6月前
|
JavaScript 安全 前端开发
TypeScript 字符串(String)如何使用?
TypeScript 字符串(String)如何使用?
124 0