1 基本类型
1.1 理解TS的数据类型
从集合的角度去理解TS的数据类型会容易理解一些。比如,数字类型(number)就是有1、1.1、1.11···、2、2.1、2.11··· 所有的数字组成的集合。字符串同理。
1.2 数据类型分类
1.2.1 JS datatype
number、string、Boolean、undefined、null、bigint、symbol、object(含Array、Function、Date···)
1.2.2 TS datatype
以上所有、void、never、enum、unknown、any、自定义类型type、interface
1.3 表示方式
- number
type A = number
const a: A = 1
- string
type A = string
const a: A = 'name'
- boolean
type A = boolean
const a: A = true(false)
- undefined
type A = undefined
const a: A = undefined
- null
type A = null
const a: A = null
- bigint
const n: bigint = 100n
console.log(n)
- symbol
const s: symbol = Symbol('s')
console.log(s)
- object
由于object太不精确,所以TS开发者一般使用<索引签名>或<Record泛型>来描述普通对象
- 索引签名
type Person = {
name: string,
age: number
}
- Record泛型
type A2 = Record<string, number>
- 普通方式
type A3 = {
// 键值可以不是字符串,但不能是undefined
[k: number]: string
// symbol 类型
[k1: symbol]: number
}
const a: A3 = {
name: 1,
123: 6, // 对象的默认操作会将键自动转化为字符串
}
const s = Symbol()
const a: A = {
[s]: 6,
}
- 用[]和Array泛型描述数组
// 方式一:泛型
const arr: Array<number> = [1, 2]
// 方式二:字面量
const arr1: string[] = ['hi', 'i']
// 方式三:单独表示变量类型
type A3 = [string, number]
// 二元组
type Two = [number, number]
// 三元组
type Three = [number, number, number]
// type number = 1 | 2 | 3 | 1.1.1 | ...
// 这种写法只不过将每个值进行了收缩
type A = [1, 2, 3]
const a: A = [1, 2, 3]
- 描述函数对象
type FnA = (a: number, b: number) => number
// ts 是松散的函数类型检查,即函数没有传形参不会报错
// 类型推导
const a: FnA = (x, y) => {
return 1
}
type FnReturnVoid = () => void
type FnReturnUndefined = () => undefined
const f1: FnReturnVoid = () => {
console.log('hi')
}
const f2: FnReturnUndefined = () => {
console.log('hi')
return undefined
}
type Person = {
name: string
age: number
sayHi: FnWithThis
}
type FnWithThis = (this: Person, name: string) => void
// 一般情况下使用this不能使用箭头函数
const sayHi: FnWithThis = function () {
console.log('hi' + this.name)
}
const x: Person = {
name: 'lwz',
age: 18,
sayHi,
}
x.sayHi('Jack')
sayHi.call(x, 'Jack')
- 描述其他对象
// 其他对象一般直接用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()
m.set('xxx', 1)
const wm: WeakMap<{ name: string }, number> = new WeakMap()
const s: Set<number> = new Set()
s.add(123)
const s1: Set<{ name: string }> = new Set()
s1.add({ name: 'lwz' })
const ws: WeakSet<string[]> = new WeakSet()
- any 和 unknown
// any 包含任何类型
// unknown 一般用于请求外界的数据时不知道类型,请求完数据之后使用断言将其类型固定
const a: unknown = 1
const b = a as number // 断言
console.log(a)
- never
// any 是全集,unknown是未知集,never是空集
type A = string | number | boolean
const a: A = 'hello' as any
if (typeof a === 'string') {
a.split('')
} else if (typeof a === 'number') {
a.toFixed(2)
} else if (typeof a === 'boolean') {
a.valueOf()
} else {
console.log('没了')
}
- 何时使用enum
// enum A {
// todo = 0,
// done,
// archived,
// deleted,
// }
// let Estatus: A = A.done
// console.log(Estatus)
enum Permission {
None = 0,
Read = 1 << 0, // 0001
Write = 1 << 1, // 0010
Delete = 1 << 2, // 0100
Managa = Read | Write | Delete, // 0111
}
type User = {
permission: Permission
}
const user1: User = { permission: 0b0101 }
// if ((user1.permission & Permission.Write) === Permission.Write) {
// console.log('有权限')
// }
// if ((user1.permission & Permission.Managa) === Permission.Managa) {
// console.log('有管理权限')
// }
if (canWrite(user1.permission)) {
console.log('拥有写权限')
}
if (canManaga(user1.permission)) {
console.log('拥有管理权限')
}
function canWrite(p: Permission) {
return (p & Permission.Write) === Permission.Write
}
function canManaga(p: Permission) {
return (p & Permission.Managa) === Permission.Managa
}
- 何时不用enum
// enum Fruit {
// apple = 'apple',
// banana = 'banana',
// pineapple = 'pineapple',
// watermelon = 'watermelon',
// }
// let f: Fruit = Fruit.apple
// f = Fruit.watermelon
type Fruit = 'apple' | 'banana' | 'pineapple' | 'watermelon'
let f: Fruit = 'banana'
f = 'pineapple'
console.log(f)
/**
* enum number √
* enum string ×
* enum other ×
*/
- type和interface两个区别
// 类型别名 alias
type Name = string
// type 里面只能是类型不能是值,而NaN就是一个值
// 0就是一个类型,表示的是一个集合里面只有一个值0
type FalseLike = 0 | '' | null | undefined | false
const a: Name = 'lwz'
// 带有属性的函数声明
type FnWithProp = {
// 类型里面用的冒号, 非类型声明用的是箭头函数
// (a: number, b: number) => number
(a: number, b: number): number
prop: string
}
const f: FnWithProp = (x, y) => {
return x + y
}
f.prop = 'hello'
console.log(f)
// type 只是一个别名,并不是真正的声明一个类型
type A = string
type B = A
const x: A = 'hello'
const y: B = x
console.log(x, y)
// interface 接口 --> 面向对象 Oriented Object Program
interface A1 {
a: number
b: string
// 索引签名
// [k: string]: number
}
type A2 = Array<string> & {
name: string
} & X
interface X {
age: number
}
interface A3 extends Array<string>, X {
name: string
}
interface Fn {
(a: number, b: number): number
xxx: number
}
const f1: Fn = (x, y) => {
return x + y
}
f1.xxx = 100
console.log(f1)
// interface D extends Date {
// // 下面这行代码会报错
// // xxx: number
// }
// const d: D = new Date()
// // d.xxx = 100
// console.log(d)
interface D extends RegExp {
// 也会报错
// xxx : number
}
// const d: D = new RegExp(/ab+c/)
// // d.xxx = 100
// console.log(d)
/**
* 总结type和interface的区别
* 1.
* type 描述所有的数据
* interface 描述对象的属性 注意: 仅限对象
* 2.
* type 给其他类型取个名字,只是一个别名
* interface 则是类型声明
*/
// type 只是一个别名, 所以下面的BB类型就相当于跳过了AA,直捣黄龙
type AA = string
type BB = AA
// interface 定义的是以真名, 所以下面D是实实在在存在的
interface D extends Date { }
type E = D
// 简化代码的作用
type Id = string | number
const user: Id = 'xxxx'
const customer: Id = 'xxxx'
export { }
- type和interface的第三个区别
// type 不可赋值,一旦定义好类型之后,后面就不可以再修改
// 而 interface 还有修改的可能
// 举例: axios 在发送请求的时候,第一个参数是url,而第二个参数是一个配置对象,如果此时使用 type 无法定义,因为不知道后端的请求参数是什么, 此时使用 interface 就是好很多
// interface 可以自动扩展
interface X {
name: string
}
interface X {
age: number
}
const a: X = {
name: 'lwz',
age: 18,
}
/**
* 区别三:
* 对外API 尽量使用 interf
* 方便扩展
* 对内API 尽量使用 type
* 防止代码分散
*/
- void
type Fn = () => void
const f: Fn = () => {
// return undefined
// return 1
return 'hi'
}
// 虽然上面的几种方式不会报错,但是在编译的时候会报错
// const a = f()
// console.log(a.toFixed())
// 但是下面这种方式不会报错
function Fn(): void {
// return 1
}
- 俩number、俩string、俩boolean
我们在创建了一个number类型的特定值后,其实就是做了以下操作:
因此: