any类型,unknown类型,never类型
介绍TypeScript的三种特殊类型,它们可以作为学习TypeScript类型系统的起点。 any /ˈeni/ 任何,unknow /ˌʌnˈnəʊn/ 未知的,never /ˈnevə(r)/ 从来没有
any 类型
本人作为小白,每天都在面向any编程,什么类型推导,什么类型声明,any 真爽(手动狗头)
基本含义
any就是百搭,啥穿在身上都能看,好不好看就不知道了(any类型表示没有任何类型限制,啥是没有类型限制,就是该变量可以赋予任意类型的值)
let a:any;
a = 1; // 能配上
a = '张三' // 能配上
a = true // 能配上
- 变量类型一旦设为any就如同闭眼买衣服,好不好看都要根据导购员的话来判断(
TypeScript会关闭这个变量的类型检测,明显的类型错误,语法不存在都不会报错提示)
let a:any = 'hello'
a(1) // 不报错
a.name = 'name' // 不报错
上述代码都不报错,真是瞎整,你包含了所有其他类型的也不能这么横啊(顶层类型)
类型推断问题
如果开发者没有指定类型、ts必须自己推断类型,如果无法推断出类型,默认给的类型就是any 举例:你去商场走进了一家服装店,店里有衣服、裤子、帽子,你不和导购说你具体要买啥,你就说了句想消费,那导购肯定是一条龙啥都给你拿,啥都让你试,导购忙活半天,你最后走了
function add(x,y){
return x + y;
}
add(1,[12]) // 默认推断any类型
虽然表面上很淡定,其实cpu都烧了(都给函数整不会了,能不报错吗)
污染问题
any关闭了类型检查后,还有一个问题,它会污染其他变量。它可以赋值给其他任何类型的变量,导致其他变量可能出现问题
let x:any = 'hello word'
let y:number;
y = a
y * 12 // y是any类型,其实是字符串不具备运算能力
y.toFixed() // toFixed是数字类型所提供的方法
上述代码只有在运行时才能看出问题,编译时不会给你提示 举例:a开发声明一个函数并且在submit时需要调用函数处理参数,b开发不知道这块逻辑,直接给赋值了个字符串'你瞅啥',提交得时候字符串执行不了啊,一直loading,导致双方大打出手。
unknown 类型
为了解决 any 类型污染其他变量的问题,TypeScript3.0引入了unknown类型。它与any含义相同,表示类型不确定,可能是任意类型,但是它的使用有一些限制,不像any那么自由,可以视为严格版的any。
unknown跟any的相似之处,在于所有类型的值都可以分配给unknown类型。
let x:unknown;
x = true; // 正常
x = 34; // 正常
x = 'hello World'; // 正常
上述行为和any赋值相同,随便来。
接下来就是不同之处,any可以随便给其他变量赋值,unknown就不能随便给其他变量赋值了
let x:unknown = {name:'name'}
x.name // 引用报错
// 调用函数报错 this expression in not callable type {} has no call signatures
// 不可调用类型{}中的表达式没有调用签名
let fn:unknown = ()=>{}
fn()
// 赋值报错 Type 'unknown' is not assignable to type 'number'
// 类型'unknown'不能赋值给类型'number'
let amount:number = x // 赋值报错
上述代码,直接调用unknown类型变量的属性和方法,或者直接当作函数执行,都会报错。
那设置成unknown类型后在什么场景使用下不报错呢?
比较运算(运算符==、===、!=、!==、||、&&、?)、取反运算(运算符!)、typeof运算符和instanceof运算符这几种,其他运算都会报错。
let a:unknown = 1;
// 报错 Operator '+' cannot be applied to types 'unknown' and '1'
// 运算符“+”不能应用于类型“未知”和“1”
a + 1;
a === 1; //正常
类型缩小
上面所介绍了unknown不能直接使用,只可进行比较运算使用,那么,怎么才能正常使用让其参与逻辑,那就是“类型缩小”
所谓类型缩小,就是缩小unknown变量的类型范围,确保不会出错
let a:number=1;
// typeof a 就是一个类型缩小的过程,让其更明确类型进而使用
if(typeof a === 'number'){
let r = a + 10 // 正常
}
let s:unknown='hello'
if(typeof s === 'string'){
// 调用字符串的方法都可以
s.length // 正确
s.replace('h','') // 正确
}
这样设计的目的是,只有明确unknown变量的实际类型,才允许使用它,防止像any那样可以随意乱用,“污染”其他变量。类型缩小以后再使用,就不会报错。
总之,unknown可以看作是更安全的any。一般来说,凡是需要设为any类型的地方,通常都应该优先考虑设为unknown类型。
在集合论上,unknown也可以视为所有其他类型(除了any)的全集,所以它和any一样,也属于 TypeScript 的顶层类型。
never 类型
为了保持与集合论的对应关系,以及类型运算的完整性,TypeScript还引入了“空类型”的概念,即该类型为空,不包含任何值。
由于不存在任何属于“空类型”的值,所以该类型被称为never,即不可能有这样的值。
let a:never;
上述代码 a 变量类型是never,也就意味着不可以直接给它赋值,否则都会报错
// Type 类型 is not assignable to type 'never'
// 类型 'srtring'/'numnber' 不能赋值给类型'never'
let a:never = 'string'; // 报错
let b:never = 123; // 报错
never类型的一个重要特点是,可以赋值给任意其他类型。
const fn:never=()=>{
console.log('触发')
}
let n:number = fn() // 正常
let s:string = fn() // 正常
为什么never类型可以赋值给任意其他类型呢?,这也跟集合论有关,空集是任何集合的子级。TypeScript就相应规定,任何类型都包含了never类型。因此,never类型是任何其他类型所共有的,TypeScript把这种情况称为“底层类型”(bottom type)。
总之,TypeScript 有两个“顶层类型”(any和unknown),但是“底层类型”只有never唯一一个。
集合论
通常指的是通过 TypeScript 的类型系统来建立类型之间的关系,这可以通过TypeScript中的泛型、联合类型、交叉类型等功能来实现。
// 联合类型
let fn = (amount:number|string) => {
console.log(amount)
}
fn(123) // 正常并输出 123
fn('123') // 正常并输出 '123'
let cancat<T> = (arr1:T[],arr2:T[]):T[]{
return [...arr1,...arr2]
}
const arrCancat = cancat([1,2,3],[4,5,6]);
console。log(arrCancat) // 输出 [1,2,3,4,5,6]
这种类型的使用方式可以看作是在 TypeScript 中建立类型之间的对应关系,类似于集合论中的概念。
结尾
上述摘要大多数来自阮一峰老师的TS教程并结合一些自己的白话理解,个人技术落后就要挨打,成长是个人的,接受一切成长道路上所收集的宝贵意见。