类型守卫
-
类型守卫的作用在于触发类型缩小。实际上,它还可以用来区分类型集合中的不同成员
-
类型守卫包括switch、字面量恒等、typeof、instanceof、in 和自定义类型守卫 简单说当一个类型是多种可能时例如’any’,‘unknown’,‘联合类型’ 等在逻辑判断时候要具体到其唯一子集可能性
- 2.1.类型判断:typeof
- 2.2.实例判断:instanceof
- 2.3.属性判断:in
- 2.4.字面量相等判断:==, =, !=, !
1. 没有使用类型守卫案例
通过断言,断言其子类型做到缩小范围不报错,虽然这种形式也可以但是需要大量断言来区分类型
const getRandomValue = (list: (string | number)[]) => {
const randomNumber = Math.random() * 10
if (randomNumber > 5) return list[0]
else return list[1]
}
const item = getRandomValue([1, 'w'])
// 直接这些写会报错
// 首先第一个问题是返回值的类型不确定是字符还是数字
// 数字是没有length 属性的所以这么写的判断ts 会报错的
// if(item.length){
// console.log(item.length)
// }else{
// console.log(item.toFixed())
// }
// 解决第一种使用类型断言
// 缺点 每一个item 都要使用对应的类型断言
if ((item as string).length) {
console.log((item as string).length)
} else {
console.log((item as number).toFixed())
}
2. 使用typeof 进行类型守卫
为什么用typeof
做类型守卫呢?因为typeof
能判断JS基本数据类型。typeof
只能识别以下类型:
- Boolean
- String
- Undefined
- Function
- Number
- Bigint
- Symbol
写法: typeof a
, a
是变量(基本数据类型)
为什么 typeof
不能识别null呢?
let a= null
typeof a;//object
null
是一个只有一个值的特殊类型,表示一个空对象引用,可以用来清空对象,它是 object
类型是历史遗留下来的问题,曾提议改为 null
类型,被拒绝了。 typeof
识别其他的类型比如数组,正则等都是 object
类型
let a =[1]
typeof a;//Object
var reg = RegExp("a","i");
typeof reg//reg
typeof
怎么起到守卫的作用呢,通过 typeof
判断变量类型然后执行相应的逻辑,具体如下:
function getType(params: string | number) {
// return params.length ? params.length : params 这么写会报错 这就是为什么要类型守卫缩小类型
if (typeof params === 'string') {
return params.length
} else {
return params
}
}
上面案例的传参都会基本类型,当传一个对象时候,我们也可以用对象中的属性来进行判断,比如:
interface A{
a:string;
}
interface B{
a:number;
}
type Class = A | B;
function getInfo(val:Class){
//判断val的属性a的类型为number类型
if(typeof val.a === "number"){
console.log('B:'+ val.a)
}
//判断val的属性a的类型为string类型
if(typeof val.a === "string"){
console.log('A' + val.a)
}
}
3. instanceof 针对类 – 缩小范围
为什么用 instanceof
呢?因为 typeof
有局限性,引用类型比如数组,正则等无法精确识别是哪一个种型, instanceof
能够识别变量(比如实例对象)是否属于这个类 instanceof
不能检测原始值类型的值,但是原始值对应的对象格式实例则可以检测。具体 instanceof
是怎么做类型守卫的呢?
写法: a instanceof b
,a是参数,b是一般都是接口类型。
interface Teacher{
name:string;
courses:string;
}
interface Student{
name:string;
study:string;
}
type Class = Teacher | Student;
function getInfo(val:Class){
//判断val的类型是否是定义的接口Teacher类型
if(val instanceof Teacher){
console.log('teacher:'+ val.courses)
}
//判断val的类型是否是定义的接口Student类型
if(val instanceof Student){
console.log('student' + val.study)
}
}
4. 获取value 值 – in
interface Teacher{
name:string;
courses:string;
}
interface Student{
name:string;
study:string;
}
type Class = Teacher | Student;
function getInfo(val:Class){
//此时val类型缩小为Teacher类型
if('courses' in val){
console.log(val.courses)
}
//此时val类型缩小为Student类型
if('study' in val){
console.log(val.study)
}
}
getInfo({ name: 'student', study: "Philosophy" });
//打印结果为Philosophy,因为传参中含有study属性,所以走了第二个判断
注意:用 in 关键字缩小数据类型必须有一个独特的属性作为判别标准,否则不能用 in 关键字
5. 字面量恒等
1.如果是字面量这种联合类型可以使用具体值去比较
2.也可用switch 来缩小类型 代替if
function getType(params: 'a' | 1) {
if (params === 'a') {
return params.length
}
return params
}
type Foo = {
kind: 'foo'; // 字面量类型
foo: number;
};
type Bar = {
kind: 'bar'; // 字面量类型
bar: number;
};
function doStuff(arg: Foo | Bar) {
if (arg.kind === 'foo') {
console.log(arg.foo); // ok
console.log(arg.bar); // Error
} else {
console.log(arg.foo); // Error
console.log(arg.bar); // ok
}
}
6. 自定义守卫 – is
TS中有一个关键字is
可以判断变量是否属于某种类型。
// 1.往往开发时候会吧一些判单同一封装 像下面的例子虽然封装了,但实际效果却报错
function isString(params: unknown) {
return typeof params === 'string'
}
function getName(params: number | string) {
// if(typeof params === "string") 可以的但是这种重复逻辑一般会封装
if (isString(params)) {
return params.length // 报错
}
}
正确写法:
1.这里的is
可以是任意类型,可以是自己定义的接口类型,这里是因为需求是string 类型,如果不做is
类型,
TS 只能推断出 isString 是一个返回布尔值的函数,而并不知道这个布尔值的具体含义’{形参} is
{类型} 的语法结构’文章来源:https://www.toymoban.com/news/detail-553216.html
function isString(params: unknown) :params is string{
return typeof params === 'string'
}
function getName(params: number | string) {
// if(typeof params === "string") 可以的但是这种重复逻辑一般会封装
if (isString(params)) {
return params.length
}
}
1. 函数参数形式
interface Teacher{
name:string;
courses:string;
}
interface Student{
name:string;
study:string;
}
const isTeacher = function (cls: Teacher | Student): cls is Teacher {
return 'courses' in cls;
}
const getName = (cls: Teacher | Student) => {
if(isTeacher(cls)) {
return cls.courses;
}
}
2. this形式
下面代码中的 User 是抽象类,不能被实例化,Staff 和 Student 都继承自 User。实例方法 isStaff 用于将类型收窄为 Staff,实例方法 isStudent 用于将类型收窄为 Student文章来源地址https://www.toymoban.com/news/detail-553216.html
abstract class User {
name: string;
constructor(name: string) {
this.name = name;
}
isStudent(): this is Student {
return this instanceof Student;
}
isStaff(): this is Staff {
return this instanceof Staff;
}
}
class Student extends User{
study: string;
constructor(name: string, study: string) {
super(name)
this.study = study
}
}
class Staff extends User {
workingYears: number;
constructor(name: string, workingYears: number) {
super(name)
this.workingYears = workingYears
}
}
function judgeClassType(obj: User) {
if (obj.isStaff()) {
// obj的类型被缩小为 Staff
} else if (obj.isStudent()){
// obj 的类型被缩小为 Student
}
}
到了这里,关于TypeScript 学习笔记(四):类型守卫的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!