前言: 本类文章初衷只用作记录个人的学习博客,哪里有漏补哪里,不做任何其他商业用途。欢迎讨论,不喜勿喷。后面要是有遗漏的相关知识点,也会相应的补上。如果本篇文章能帮到你,我也会很愉快,共勉😁
目录
- 编译环境
- 枚举
- [any,unknown,void,never]
- 参考文献
编译环境
全局安装Typescript:
- npm install typescript -g
全局安装ts的编译工具
- npm install ts-node -g
枚举
枚举允许开发人员定义一组命名常量。使用枚举可以更轻松地记录意图,或创建一组不同的案例。TypeScript 提供基于数字和字符串的枚举。TS目前有数字和字符串两张枚举。
数字枚举
ts
enum Direction {
Js,
Ts,
Dart
}
console.log(Direction.Js, Direction.Ts, Direction.Dart) // 0 1 2
const respond = (message: Direction): void => {
console.log('print:', message)
}
respond(Direction.Js) // 0
数字枚举初始化为0,并且以下的所有成员都会自动递增。
ts
enum Direction {
Js,
Ts = 2,
Dart
}
console.log(Direction.Js, Direction.Ts, Direction.Dart) // 0 2 3
可在任意成员更改其的默认下标,更改后会从当前位置以更新后的值作为初始值继续递增。
字符串枚举
ts
enum Direction {
Js = 'JavaScript',
Ts = 'TypeScript',
Dart = 'Dart'
}
console.log(Direction.Js, Direction.Ts, Direction.Dart) // JavaScript TypeScript Dart
字符串枚举其可读性和意图相较数字枚举更佳。
异构枚举
ts
enum Direction {
Js,
Ts = 10 * 30,
Dart = Js | Ts,
Java = 5,
Python = 'test',
Go = 'test'.length
}
console.log(
Direction.Js, // 0
Direction.Ts, // 300
Direction.Dart, // 300 5 test 4
Direction.Java, // 5
Direction.Python, // test
Direction.Go) // 4
异构枚举其实就是在一个枚举中同时存在数字和字符串。不过正如官方所言,除非你真的想以巧妙的方式利用JavaScript的运行时行为,否则建议你不要这样做。
运行/编译时的枚举
ts
enum Direction {
Js,
Ts,
Dart
}
const callback = (obj: { Js: number }): number => {
return obj.Js
}
callback(Direction);
如上,枚举在运行时是一个真实存在的对象,所以它可以作为参数传递给函数。
ts
enum Direction {
Js = 'JavaScript',
Ts = 'TypeScript',
Dart = 'Dart'
}
// 相当于 type Prints = 'Js' | 'Ts' | 'Dart'
type Prints = keyof typeof Direction
const callback = (message: Prints): string => {
return `to ${Direction[message]}`
}
console.log(callback('Ts')) // to TypeScript
使用keyof typeof
获取enum的所有key。
反向映射
先声明一个普通的数字枚举
ts
enum Direction {
Js,
Ts,
Dart
}
通过key映射value
ts
console.log(Direction['Dart']) // 2
也可以通过value映射key
ts
console.log(Direction[0]) // Js
任意类型
any和unknown
any和unknown都可以被赋予任何类型的值,那它们有什么不一样呢? 先提结论:
- any: 会绕过所有的ts验证
- unknown:仅绕过赋值验证
any代表着任意,unknown代表着未知,看以下代码:
ts
// 绕过类型验证和赋值验证
let a: any = 10
// 只绕过赋值验证
let b: unknown = 20
if (a >= 10) console.log(a) // 10
if (b >= 10) console.log(b) // 会报错,b类型为未知
我们发现,在赋值时,一切都是正常的。any和unknown都能被赋值为一个数字,但是将两个变量放入if中时,a正常执行了,而b会有报错,提示b的类型为未知。因为在判断时,编译器无法确定b的类型,故而报错。 由此可见,unknown在赋值方面和any一样,可以赋任意的值,但是它如果要进行类型验证时,则会无法通过。
ts
if ((b as number) >= 10) console.log(b) // 20
通过类型断言,告诉编译器,我能确定b就是一个number类型,则正常通过。再看一个:
ts
function invokeAnything(callback: any) {
callback()
}
invokeAnything(1)
上面这段代码可以正常编译(哪怕callback传递的不是一个函数),因为callback被指定了any类型,会绕过ts的所有类型检测。我们改为unknown:
ts
function invokeAnything(callback: unknown) {
// 编译报错: callback类型为未知
callback()
}
invokeAnything(1)
如果没告诉编译器callback的类型,在编译时就会报错。以下代码即可通过:
ts
function invokeAnything(callback: unknown) {
if (typeof callback === 'function') {
callback()
}
}
invokeAnything(1)
因此,最后的总结是:
- 可以将任何内容分配给any类型,并且可以执行任何操作any。
- 可以将任何内容分配给unknown类型,但必须进行类型断言或类型检查才能继续操作unknown。
void和never
void
void类型表示没有任何类型,声明一个void类型的变量基本没什么用,因为它只能被赋予undefined。更多是应用在一个函数没有返回值时,它的返回值类型就会是void。
ts
let a: void = undefined
const fn = (message: string): void => {
console.log(message)
}
never
never代表永远不会发生的类型,常用场景如:
- 一个从来不会有返回值的函数。
- 一个总是会抛出错误的函数。
ts
const fn1 = (): never => {
while(true) {}
}
const fn2 = (): never => {
throw new Error('Network Error')
}
除了never,任何值都无法给never类型的变量赋值。
ts
let a: never = 123 // Error: 不能将类型number的值分配给never
let foo: never = (() => { // ok,作为函数返回类型的never
throw new Error('Error')
})()
void和never的区别
当一个函数返回空值时(哪怕没写return返回的也是undefined),它的返回值为void类型。但是,当一个函数永不返回时(或者总是抛出错误),它的返回值就为never。void类型可以被赋值undefined以及一个返回undefined的函数,而never除了本身,其他任何类型都不能赋值。
参考文献
dmitripavlutin.com/typescript-... jkchao.github.io/typescript-...