建议先从 typeScript从入门到进阶-基础篇(一)开始学习
基本类型
ES6的数据类型 | TS的数据类型 |
---|---|
Boolean | Boolean |
Number | Number |
String | String |
Array | Array |
Function | Function |
Object | Object |
Symbol | Symbol |
undefind | undefind |
null | null |
- | void |
- | any |
- | never |
- | 元组 |
- | 枚举 |
- | 高级类型 |
类型注解
- 作用:相当于强类型语言中的类型声明
- 语法:(变量/函数):type
// 原始类型
let bool: boolean = true
let num: number = 124
let str: string = 'abc'
// 数组
let arr1 = number[] = [1, 2, 3]
let arr2 = Array<number> = [1, 2, 3]
let arr3 = Array<number | string> = [1, 2, 3, '4']
// 元组 一种特殊的数组,限定了元素的类型和个数
let tuple: [number, string ] = [0, '1']
// 元组的越界问题:
tuple.push(2)
console.log(tuple) // [0, '1', 2]
tuple[2] // 报错不能访问,开发时不建议这样做
// 函数
let add = (x: number, y: number): number => x + y
let add = (x: number, y: number) => x + y // 返回值类型number可以省略
let compute: (x: number,y: number) => number // 函数类型的定义
compute = (a, b) => a + b // 函数的实际定义中,形参可以和定义不一样,也可以不用指定具体类型
// 对象
let obj: {x: number, y: number} = {x: 1, y: 2}
obj.x = 3
// symbol 具有唯一的值
let s1: symbol = Symbol()
let s2 = Symbol()
console.log(s1 === s2 ) // false
// undefind, null
// 一旦被赋值成undefind 就不能再赋值任何类型了
let un: undifind = undefind
let nu: null = null
// 在ts官方的文档中,undefind 和 null 是任何类型的子类型,说明可以被赋值成其他类型
// 这里需要做一些设置:
// 在tsconfig.json 设置"strictNullChecks": false,就允许赋值了
// 但是需要注意: 声明需要加上null和undefind才能通过类型检查
let num: number | null | undefind = 124
// void
// 在ts中,void 表示一种没有任何返回值的类型
// 在js中,void 是一种操作符,可以让任何表达式返回undefind
let noReturn = () => {} // 一个没有任何返回值的函数
void 0 // undefind 为什么会有这种操作?
// 在js中,undefind不是一个保留字,我们甚至可以自定义一个undefind去覆盖全局的undefind;
(function() {
var undefind = 0
console.log(undefind)
})()
// any
在ts中不指定变量的类型,默认就是any类型,不建议使用any
let x
x = 1
x = []
x = '2'
// never
// 表示永远不会有返回值的类型
let error = () => { // 函数抛出异常
throw new Error('error')
}
let endLess = () => { // 死循环函数
while(true){}
}
枚举类型
一组有名字的常量集合
- 枚举成员的值是只读类型的,一旦定义了不能修改
- 实现原理:反向映射,枚举被编译成了一个对象,枚举成员的名称被作为了key,枚举成员的值被作为了value,value又作为key,成员的名称又被作为value;
- 适用情况:将程序中不容易记忆的硬编码,或者在未来可能改变的常量抽取出来,定义成枚举类型,可以提高程序的可读性和可维护性
enum {
Reporter = 1,
Developer
}
最终被编译成:
"use strict"
var Role;
(function(Role){
Role[Role["Reporter"] = 1] = "Reporter"];
Role[Role["developer"] = 1] = "developer"];
}
)(Role || {Role = {})
// 数字枚举
enum {
Reporter = 1, // 可以给默认值
Developer
}
// 字符串枚举(不能进行反向映射)
enum Message {
Success = "恭喜,恭喜",
Fail = "再接再厉"
}
// 异构枚举 (容易引起混淆,不建议使用)
enum Answer {
N,
Y = 'yse'
}
// 枚举成员
类型:1.const 常量枚举,常量枚举会在编译时计算出结果,然后以常量的形式出现在运行时环境
包括三种情况:
- 没有初始值的情况
- 对已有枚举成员的引用
- 常量的表达式
2.computed number,一定要被赋予初始值;
需要被计算的枚举成员,非常量的表达式,不会再编一阶段进行计算,而会保留到程序的执行阶段
enum Char {
// const
a,
b = Char.a,
c = 1 + 3,
// computed
d = Math.random(),
e = '123'.length
}
// 常量枚举:用const声明的枚举,在编译阶段会被移除
- 当不需要对象,只需要一个对象的值的时候可以用常量枚举
const enum Month = {
Jan,
Feb,
Mar
}
let month = [Month.Jan, Month.Feb, Month.Mar]
// 枚举类型
// - 在某些情况下,枚举和枚举成员都可以作为一种单独的类型存在
enum E { a, b } // 没有初始值
enum F { a = 0, b = 1 } // 所有初始值都是数字枚举
enum G { a = 'apple', b = 'banana' } // 所有初始值都是字符串枚举
let e: E = 3 // 可以把任意的number类型赋值给枚举类型,它的取值也可以超出枚举成员的定义
let f: F = 3
e === f // 报错;两种不同的枚举类型成员是不可以比较的
let e1: E.a === 1
let e2: E.b
e1 === e2 // 报错;两种不同的枚举类型成员是不可以比较的
let e3: E.a === 1
e1 === e3 // true 相同的枚举类型成员可以比较
let g1: G = G.a // 字符串枚举的取值必须是枚举成员的类型
let g2: G.a = G.A // 只能是自身
接口
对象类型接口
- 接口可以用来约束对象,函数,类的结构和类型,是一种代码协作的契约,必须遵守,且不能改变;
interface List {
id: number,
name: string
}
interface Result {
data: List[]
}
function render(result: Result){
result.data.forEach((value) => {
console.log(value.id,value.name)
})
}
let result = { // 假设result是从后端返回的接口
data: [
{id: 1, name: 'A', sex: 'male'}, // 只要满足要求,传入多余的字段也可以通过类型检查
{id: 2, name: 'B'}
]
}
render(result) // 没毛病
// 用对象字面量的方式ts会报错
render({ // 假设result是从后端返回的接口
data: [
{id: 1, name: 'A', sex: 'male'}, // 只要满足要求,传入多余的字段也可以通过类型检查
{id: 2, name: 'B'}
]
})
绕过这种检查的方式有三种:
1.将对象字面量赋值给一个变量,如上;
2.使用类型断言, as Result,或在前面加上<Result>
- 这两种方法是等效的,建议使用第一种方法,<Result>在React会产生歧义
明确的告诉编译器,传入的对象类型就是Result,这样编译器就会绕过类型检查
render({ // 假设result是从后端返回的接口
data: [
{id: 1, name: 'A', sex: 'male'}, // 只要满足要求,传入多余的字段也可以通过类型检查
{id: 2, name: 'B'}
]
} as Result)
render(<Restul>{ // 假设result是从后端返回的接口
data: [
{id: 1, name: 'A', sex: 'male'}, // 只要满足要求,传入多余的字段也可以通过类型检查
{id: 2, name: 'B'}
]
})
3.使用字符串索引签名
interface List {
id: number,
name: string,
[x: string]: any // 字符串索引签名, 含义是:用任意的字符串去索引List,可以得到任意的结果,这样List可以支持多个属性了
}
接口成员属性:
1. 可选属性,属性后加 ?
interface List {
id: number,
name: string,
age?: number // 可有可无
}
2. 只读属性
interface List {
readonly id: number, // id一般是只读属性
name: string
}
可索引类型的接口
- 在不确定接口返回来的属性的个数,就可定义可索引类型的接口,可以用数字去索引,也可用字符串去索引
// 数字索引类型的接口
interface StringArray {
[index: number]: string // 用任意的数字去索引stringArray,都会得到一个string,就相当于声明了一个字符串类型的数组
}
// 字符串索引类型的接口
interface Names {
[x: string]: string; // 用任意字符串去索引Names,得到的都是一个string,这样声明后,就不能声明number类型的成员了
// y: number; // 不被允许
}
两种索引签名是可以混用的:
interface Names {
[x: string]: string, // [x: string]: any,
[z: number]: string // 注意:数字索引签名的返回值一定要是字符串索引签名返回值的子类型,因为js会进行类型转换,将number转换成string,这样就能保持类型的兼容性
}