JS/TS 中有哪些数据(data)类型(type)
- JS
- null, undefined, string, number, boolean, bigint, symbol, obejct(含 Array、Function、Date...)
- TS
- 以上所有,加上 void, never, enum, unknown, any,再加上自定义类型 type, interface
如何理解 TS 的数据类型
和 JS 的区别在于,JS 说到数据类型的时候,是说这个值是字符串,这个值就是数字,
那么 TS 呢,指的是一类数据的类型,而不是一个,所以说得从集合的角度来理解
例如:
- number 是指 1 | 1.1 | 1.2 | .... | 2 | ...
- string 是指 a | b | ... | z | ...
- boolean 是指 true | false
- Object 是指 {...} | Array | Function | String | Number | ... | RegExp | ...
注意:Object 这里 为什么还会有 String,Number?
这里涉及到 JS 的历史遗留知识,42 VS new Number(42)
基本没有人会用 new Number(42) 来表示“42”
42 就是一个普通的值
而 new Number(42),则表示这是一个对象:{ constructor: ...., toFixed: ..., toString: ..., valueOf: function() { return 42 } }
但是 (42).toFixed(),为什么可以正常运行,明显不合逻辑
当然是因为 JS 的包装对象,在你写上述代码的时候,JS 帮你做了这四步:
- let temp = new Number(42); 2. value = temp.toFixed(2); 3. 删除 temp; 4. 返回 value;
所以 JS 中的 Number, String, Boolean 只用于包装对象,正常来说不会用到他们,当然在 TS 里也不用
那么问题又来了,
var n = 42; n.xxx = 2; console.log(n.xxx)
,会打印出什么呢?
用类型签名和Rercord描述对象
type A = Object
const a:A = 1
const a1:A = () => {}
const a2:A = {}
const a3:A = /a+b/
这和 any 有什么区别嘛,所以我们一般在 TS 里不用 Object
那么用什么呢?
- 用 class / constructor 描述,例如:
const a: Function = () => {}
- 用 type 或 interface 描述,例如:
type Person = { name: string; age: number; }
,我们比较常用的是索引签名type A = { [k: string]: number }
(这个语法特别丑),所以我们一般用Record<string, number>
思考:key 的类型可以不是 string/symbol 吗?
试一下就知道了
type A = Record<number, number>
const a: A = {
name: 1,
123: 6,
}
当 key 的类型是 number 的时候,虽然 js 不支持,但 ts 只会做字面量上的检查
用[]和Array泛型来描述数组对象
type A = string[]
const a: A = ['h', 'i']
// 等价于
type A = Array<string>
const a: A = ['h', 'i']
type D = [string, 'string'] // 二元组
const d: D = ['h', 'i']
type D = [string, 'string', 'string'] // 三元组
const d: D = ['h', 'i', 'n']
type E = [number[], string]
const e: E = [[1,2,3,4], 'abc']
思考
type A = [1,2,3]
const a: A = ???
只能为 [1,2,3],注意这里的 type A 不是值,1 也是类型,是 number 的子集
描述函数对象
type FnA = (a, number, b: number) => number
const fnA: FnA = () => 1 // 这里即使不写参数也可以,ts 在这里的检查是松散的,后面会在类型兼容中细讲
返回值为 void 和 undefined 的时候
type FnA = () => void
const fnA: FnA = () => {}
type FnB = () => undefined
const fnB: FnB = () => {} // 这里报错,似乎与 js 的表现不太一样,按理来说 js 的函数不写返回值,那么默认就是返回 undefined
所以在实践中,我们的函数没有返回值,我们一般写 void
怎么描述函数的 this
// 这里名称必须为 this
type FnWithThis = (this: {name: string; age: number;}, name: string) => void
const fnWithThis: FnWithThis = function() {
console.log('hi', this.name)
}
// 此时调用只能显示指定 this
fnWithThiscall({name: 'hh', age: 18}, 'jack')
由于 Function 不够精确,所以 TS 开发者一般用
() => ?
来描述函数
其他对象一般直接用 class 来描述
其他对象
const d: Date = new Date()
const r: RegExp = /ab+c/
const r2: RegExp = new RegExp('ab+c')
const m: Map<string, number> = new Map()
const wm: WeakMap<{name: string}, number> = new WeakMap()
const s: Set<number> = new Set()
const ws: Set<{name: string}, number> = new WeakSet()
any、unknown 是什么?
any 更像是所有的值的集合(也不准确,之后会讲),unknown 和 any 差不多,只是你不收窄类型你就无法使用
never 是什么
可以理解为空集,哪有什么用呢?用来做检查
type A = string & number
// string 和 number 的交集是什么呢?就是 never
// 一般没人会这么写
type B = string | number | boolean
const b:B
if (typeof b === 'string') {
a.split('')
} else if (typeof b === 'number') {
a.toFixed(2)
} else if (typeof a === 'boolean') {
a.valueOf()
} else {
console.log('没了')
// 此时这里的 a 就是 never 了,
// 上面已经把该有的可能都排除了,但是如果让你告诉我这里的 a 是什么类型,怎么解释
// 为了解释这种类型,所以才有 never
}
关于 JS 类型与 TS 类型的区别
JS 这门语言其实并没有提供类型相关的关键字(如小写的 number、string、boolean),只是提供了 typeof 让我们获取变量的类型,所以我认为 JS 对类型非常不重视。
而 TS 则将 JS 中的类型提升到很高的位置,高到如果你把类型写错了代码就不能通过编译。因此这两门语言中的类型其实没有可比性,只是看起来有重叠部分而已。如果一定要比较,我会这么说:
- 粒度不同:在 JS 中的类型 number 不可再细分,而 TS 中的 number 可以看做 1、1.1、2、2.3 等无数个小类型的联合。
- 可变性不同:JS 中的变量类型是可变的,一个 number 随时可以变成一个 string;TS 中除了 any,其他类型要么是不可变的,要么就算可变也是有限制的。