当前位置: 代码网 > it编程>编程语言>Javascript > TypeScript类型any never void和unknown使用场景区别

TypeScript类型any never void和unknown使用场景区别

2024年05月18日 Javascript 我要评论
前言不知道初学typescript的同学会不会和我一样被any,unknown,never和void这几个类型搞得晕头转向呢?如果你也有同样的困惑,那么就请跟着本篇文章彻底搞懂这四种类型的区别吧。an

前言

不知道初学typescript的同学会不会和我一样被anyunknownnevervoid这几个类型搞得晕头转向呢?如果你也有同样的困惑,那么就请跟着本篇文章彻底搞懂这四种类型的区别吧。

any

首先给大家介绍的是any类型,我相信一些从javascripttypescript的同学一定不会对这个类型感到陌生,因为它是我们将javascript代码重构为typescript代码的银弹,甚至有些程序员由于过度依赖any类型活生生将typescript写成了anyscript

用法

当我们将某个变量定义为any类型后,typescript将会跳过对这个变量的类型检查:

let something: any = 'hello world!' 
something = 18 // ok! ts不会管any的类型检查
something = true // ok! ts不会管any的类型检查

在上面的代码中一般来说如果something被初始化为一个字符串类型后,是不可以被赋值为number或者boolean类型的,不过由于我们声明了它的类型是any所以typescript就完全不管这个对象的类型检查了。除了这个,你还可以随意访问这个any对象上面的任意属性,即使它们不存在:

let something: any = 'hello world!'
something.notexistmethod() // ok! 
something.notexistproperty.name() // ok!

在上面的代码中我们可以访问any类型对象的任意属性,并且这个any是具有传递性的,也就是说something后面无论跟了多少个属性访问,它们的类型都是any

使用场景

any一般的使用场景有下面这些:

  • 代码从js迁移到ts:这个时候使用any我们可以将重构快速推进而不用陷入无边无尽的类型错误里面去。这里值得一提的是any只能作为临时过渡方案,我们最后的结果一定是确保代码尽可能少any的出现
  • 我们不关心对象的类型:例如我们实现了一个print函数,这个函数底层调用console.log,这个时候其实我们不需要关心传进来的具体数据类型是什么,我们只需要一股脑将它传递给console.log函数即可,这个时候我们就可以将函数的参数类型设置为any

类型缺失或者补全困难:这种情况一般发生在我们使用了第三方js编写库的时候,我们没有办法知道某个导出的函数的具体类型,这时我们需要定义一些全局的类型垫片:

// shim.d.ts
declare module "unknown-type-lib" {
  export function somefunction(input: string): any
}

注意:虽然any很方便,可是我们还是需要想办法避免代码中出现any类型,这是因为any跳过了所有的类型检查,而这会给我们带来一些潜在的安全问题。最坏的情况是整个代码除了any没有其它有意义的类型,这个时候还不如直接编写javascript代码来得实在,既然我们选择了typescript,我们还是希望它可以真真实实为我们带来收益的,因此当我们碰到一些很难编写的类型时,最好的做法不是写any,而是询问一些资深的工程师或者网上查找资料来解决这个问题。

unknown

上面我们说到了any会跳过所有的类型问题,而这其实会为日后的代码维护和开发埋下巨大的安全隐患。为了解决any的问题,typescript在3.0版本引入了unknown类型,它可以理解为类型安全的(type-safe)any

用法

any一样,任何类型都可以赋值给unknown类型的对象:

let vany: any = 'hello world!' // ok! any对象接受任何类型
let vunknown: unknown = 'hello world!' // ok! unknown对象接受任何类型的对象

any不一样,unknown类型的对象不可以直接赋值给其它非unknownany类型的对象,并且不可以访问上面的任何属性:

let vany: any = 'hello world!'
let vunknown: unknown = 'hello world!'
let vnumberforany: number = vany // ok! any可以直接赋值给其它任意类型
let vnumberforunknown: number = vunknown // error! unknown不可以直接赋值给其它非any和unknown类型的对象
vany.tolocalelowercase() // ok! any可以访问所有的属性
vunknown.tolocalelowercase() // error! unknown对象不可以直接访问上面的属性

那么应该怎样才能使用unknown类型的变量呢?答案很简单,那就是你需要先推断出对象的类型,才能使用,推断的方式有很多种,包括typeofas assertion等其他type guard方法:

let vunknown: unknown = 'abc'
// 使用typeof推断出vunknown的类型是string
if (typeof vunknown === 'string') {
    vunknown.tolocaleuppercase() // ok! 因为能进入这个if条件体就证明了vunknown是字符串类型!
}
let vnumberforunknown: number = vunknown as number // unknown类型一定要使用as关键字转换为number才可以赋值给number类型

使用场景

由于unknown基本可以替代any,所以在任何any适用的场景,都应该优先使用unknown。使用了unknown后,我们既允许某个对象储存任意类型的变量,同时也要求别人在使用这个对象的时候一定要先进行类型推断。

never

never不像前面那几个类型一样常用,甚至有些同学可能一开始压根就不知道这个类型存在的意义是什么。我们知道typescript在解析我们的代码时会对代码进行类型推断,并且在代码流不断深入的时候,类型会从较为宽泛的类型(例如any)一直推断到较为具体的类型,而这么推断下去是会有个终点,这个终点就是不存在的,不可能发生的类型,也就是类型系统的底部类型(bottom type),而never就是typescript的底部类型。

用法

never类型只接受never类型的对象,甚至万金油any类型都不可以赋值给never类型。

let vany: any = 1
let vnever: never =  vany // error! never除了自己谁不都接受!

一般当我们想表示某个函数永远不会返回时,可以使用never类型,例如下面的例子:

// 因为这个是无限循环,我们可以使用never作为返回值表示它永远不会返回
function foreverloop(): never {
  while(true) {}
}
// 因为这个函数会抛出异常,所以也是不会返回的
function crashfunc(): never {
  throw new error('this function will crash')
}

使用场景

never类型的一个最大的作用就是帮我们对类型进行exclusive check,例如下面这个例子:

interface qa {
    kind: 'qa'
    bug: number
}
interface developer {
    kind: 'developer'
    hair: number
}
type techdude = qa | developer
function printtechdude(h: techdude) {
    if (h.kind === 'qa') {
        console.log(h.bug)
    } else if (h.kind === 'developer') {
        console.log(h.hair)
    } else {
        let exclusivecheck: never = h // 由于这个代码永远也到达不了,所以h的类型被自动推断为never
    }
}

上面的代码现在是没有问题的,不过假如某一天我们新增了一个新的pm类型,而忘记在printtechdude函数里面处理这个新类型的话,上面的代码会报错:

interface qa {
    kind: 'qa'
    bug: number
}
interface developer {
    kind: 'developer'
    hair: number
}
interface pm {
    kind: 'pm'
    features: number
}
// techdude多了一个pm类型
type techdude = qa | developer | pm
function printtechdude(h: techdude) {
    if (h.kind === 'qa') {
        console.log(h.bug)
    } else if (h.kind === 'developer') {
        console.log(h.hair)
    } else {
        let exclusivecheck: never = h // error! 因为pm类型不可以赋值给never类型
    }
}

上面代码报错的原因是techdude这个类型在else这个代码体里面已经被typescript收拢为pm类型,所以不再是never类型了。要去掉这个错误,我们需要在printtechdude函数里面额外加多一个else if(h.kind === 'pm')的判断:

function printhuman(h: techdude) {
    if (h.kind === 'qa') {
        console.log(h.bug)
    } else if (h.kind === 'developer') {
        console.log(h.hair)
    } else if (h.kind === 'pm') {
        console.log(h.features)
    }else {
        let exclusivecheck: never = h
    }
}

也正是因为exclusive check的存在我们才可以在类型变化的时候及时发现,避免问题留到了线上环境。

void

void其实可以理解为null和undefined的联合类型,它表示空值。

用法

我们一般不会声明某个值的类型为void,因为它表示这个值只能是undefined或者null(strictnullchecks没被指定):

let vvoid: void = undefined

void一个更加常见的使用场景是表示某个函数没有任何返回值:

function noreturnvalue(): void {
  console.log('hello')
  // 代码没有任何返回值,所以这个函数的返回值是void
}

使用场景

这里只想说明一下voidnever的区别。void表示空值,也就是null或者undefined,而never则表示永远都不会出现的值

总结

本篇文章通过例子给大家介绍了typescript中几个容易混淆的类型anyunknownnever void,希望能帮助有需要的人解答到疑惑,更多关于typescript类型使用场景的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com