基本类型
- string:字符串
- number:数字
- boolean:布尔值
- array:数组
- object:对象
- null 和 undefined:是所有其他类型的子类型,它们可以赋值给其他类型
- 设置
"strictNullChecks": true// 开启严格空类型检查 null 和 undefined只能赋值给自身
- 设置
其他常用类型
- 联合类型(|):多种类型任选一个,配合类型保护进行判断
- 类型保护:当某个变量进行类型判断之后,在判断的语句中,可以使用
typeof进行简单类型检查,确定类型
- 类型保护:当某个变量进行类型判断之后,在判断的语句中,可以使用
- 字面量类型:使用一个值进行约束
- 元祖类型(Tuple):一个固定长度的数组,并且数组中的每一项的类型都是确定的
- void:用于约束函数的返回值,表示该函数没有任何返回值
- never:用于约束函数的返回值,表示该函数没永远不可能结束
- any:任意类型,对该类型的所有值都可以赋值,ts不进行类型检查
类型别名
对已知的类型定义名称
type 类型名 = ...
函数类型声明
函数重载:在函数实现之前,对函数调用的多种情况进行声明
/**
* 得到a和b拼接的结果
* @param a
* @param b
*/
function com(a: string, b: string): string
/**
* 得到a*b的结果
* @param a
* @param b
*/
function com(a: number, b: number): number
function com(a: number | string, b: number | string): number | string {
if (typeof a === 'number' && typeof b === 'number') {
return a * b
}
if (typeof a === 'string' && typeof b === 'string') {
return a + b
}
throw new Error("参数类型不匹配");
}
const result = com(1, 2)
console.log(result);
可选参数:可以在某些参数名后加上问号,表示该参数可以不用传递,可选参数必须在参数列表的末尾
function sum(a: number, b: number, c?: number): number {
// 类型保护
if (c) {
return a + b + c
}
else {
return a + b
}
}
sum(1, 2)
sum(1, 2, 3)
扩展类型
扩展类型:类型别名、枚举、接口、类
- 枚举类型
- 枚举通常用于约束某个变量的取值范围
- 字面量和联合类型配合使用,也可以达到同样的目标
let sex: '男' | '女'- 存在的问题:
- 在类型约束位置,会产生重复代码。可以使用类型别名解决该问题。
- 逻辑含义和真实的值产生了混淆,会导致当修改真实值的时候,产生大量的修改。
- 字面量类型不会进入到编译结果。
- 存在的问题:
-
枚举
定义一个枚举:
enum 枚举名{ 枚举字段 = 值 ... } //例子 enum Sex { male = '男', female = '女' } let sex: Sex sex = Sex.male console.log(sex); // 男枚举会出现在编译结果中,编译结果中表现为对象
枚举的规则:
- 枚举的字段值可以是字符串或者数字
- 枚举数字的值会自动增加
- 被数字枚举约束的变量,可以直接赋值为数字
- 数字枚举的编译结果和字符串的枚举有差异
最佳实践:
- 尽量不要在一个枚举中既出现字符串字段,又出现数字字段
- 使用枚举时,尽量使用枚举字段的名称,而不使用真实的值
模块化
| 配置名称 | 含义 |
|---|---|
| module | 设置编译结果中使用的模块化标准 |
| moduleResolution | 设置解析模块的模式 |
| noImplicitUseStrict | 编译结果中不包含"use strict" |
| removeComments | 编译结果移除注释 |
| noEmitOnError | 错误时不生成编译结果 |
| esModuleInterop | 启用es模块化交互非es模块导出 |
前端领域中的模块化标准:ES6、commonjs、amd、umd、system、esnext TS中的模块化语句
TS中如何书写模块化语句
- TS中导入和导出模块,统一使用ES6模块化标准
TS中如何书写Commonjs
导出:export = ??? 导入:import ??? = require('???')
接口和类型兼容性
扩展类型-接口
扩展类型:类型别名、枚举、接口、类
TypeScript的接口:用于约束类、对象、函数的标准
- 接口约束对象
// 类型别名
// type User = {
// name: string
// age: number
// }
//接口
interface User {
name: string,
age: number
}
let u: User = {
name: 'zhangsan',
age: 10
}
- 接口约束函数
// 类型别名
// type Condition = (n: number) => boolean
// 接口
interface Condition {
(n: number): boolean
}
function sumFun(numbers: number[], callback: Condition) {
let s = 0
numbers.forEach(n => {
if (callback(n)) {
s += n
}
})
return s
}
console.log('sumFun', sumFun([1, 2, 3, 4], n => n > 3))
接口继承
可以通过接口之间的继承,实现多种接口的组合
interface A {
T1: string
}
interface B {
T2: number
}
interface C extends A, B {
T3: boolean
}
let a: C = {
T1: '11',
T2: 22,
T3: true
}
使用类型别名可以实现类似的组合效果,需要通过&,它表示交叉类型
type A = {
T1: string
}
type B = {
T2: number
}
type C = {
T3: Boolean
} & A & B
let a: C = {
T1: '11',
T2: 22,
T3: true
}
两者的区别:
- 子接口不能覆盖父接口的成员
- 交叉类型会把相同成员的类型进行交叉
readonly修饰符
interface User {
readonly id: string,
name: string,
age: number
}
let u: User = {
id: '1',
name: 'zhangsan',
age: 10
}
const arr: readonly number[] = [1, 2, 3]
const arr: ReadonlyArray<number> = [1, 2, 3]
类型兼容性
B->A 如果能完成赋值,则B和A类型兼容
鸭子辨型法(子结构辨型法):目标类型需要某一些特征,赋值的类型只要能满足该特征即可
- 基本类型:完全匹配
- 对象类型:鸭子辨型法
类型断言(as):
TypeScript中的类型断言是一种告诉编译器变量类型的方法。它有两种形式:尖括号语法和as语法。尖括号语法在React中会与JSX语法冲突,因此建议使用as语法。
- 对象类型(鸭子辨型法)
interface Duck {
sound: '嘎嘎嘎',
swin(): void
}
let person = {
name: '伪装成鸭子的人',
age: 1,
// 断言
sound: '嘎嘎嘎' as '嘎嘎嘎',
swin() {
console.log(this.name);
}
}
// person可以赋值
let duck: Duck = person
- 函数类型
- 参数:传递的参数可少不可多
- 返回值:要求返回的必须返回,不要求返回的,无所谓
TS中的类
属性
使用属性列表来描述类中的属性
属性的初始化检查
属性的初始化位置:
- 构造函数中
- 属性默认值
属性可以修饰为可选的
属性可以修饰为只读的
使用访问修饰符
访问修饰符可以控制类中的某个成员的访问权限
- public:默认的访问修饰符,公开的,所有的代码均可访问
- private:私有的,只有类的成员可以访问
- protected:protected 修饰符与 private 修饰符的行为很相似,但有一点不同,protected 成员在派生类中仍然可以访问
class Person {
readonly id: number // 永远不能改变 + readonly 只读
name: string
age: number
gender: '男' | '女' = '男'
pid?: string // 可选属性,不会初始化
private _publishNum: number = 3 // 每天一共可以发布多少篇文章 内部访问
private _curNum: number = 0 // 当前可以发布的文章数量 内部访问
constructor(name: string, age: number, gender: '男' | '女') {
this.id = Math.random()
this.name = name
this.age = age
this.gender = gender
}
publish(title:string) {
if(this._curNum < this._publishNum){
console.log(this.name + '发布了一篇文章')
this._curNum++
}else{
console.log('今日发布的文章数量已达到上限')
}
}
}
const per = new Person('张三', 20)
属性简写:如果参数直接赋值 语法糖 - 加 public 简写
class Person {
readonly id: number // 永远不能改变 + readonly 只读
constructor(public name: string, public age: number) {
this.id = Math.random()
this.name = name
this.age = age
}
}
访问器
作用:用于控制属性的读取和赋值
class User {
readonly id: number //不能改变
pid?: string
constructor(public name: string, private _age: number) {
this.id = Math.random();
}
set age(value: number) {
if (value < 0) {
this._age = 0;
}
else if (value > 200) {
this._age = 200;
}
else {
this._age = value;
}
}
get age() {
return Math.floor(this._age);
}
}
const u = new User("aa", 22);
u.age = 1.5;
console.log(u.age);
泛型
泛型是一种特殊的类型,它允许我们创建可以在多个类型之间共享的可重用代码块。泛型可以用于函数、类、接口、类型别名等。在函数中,泛型可以用来指定参数类型和返回值类型。
在函数中使用泛型
在函数名之后写上<泛型名称>
- 很多时候,TS会智能的根据传递的参数,推导出泛型的具体类型
- 如果无法完成推导,并且有没有传递具体的类型,默认为空对象
- 泛型设置默认值
<泛型名称 = 类型>
以下代码定义了一个泛型函数identity,它接受一个参数并返回该参数
function identity<T>(arg: T): T {
return arg;
}
在这个例子中,T是一个类型参数,它可以用来指定arg的类型和返回值的类型。当我们调用identity函数时,我们可以传递任何类型的参数,并且TypeScript会自动推断出arg的类型和返回值的类型。例如:
let output1 = identity<string>("hello"); // output1的类型为string
let output2 = identity<number>(123); // output2的类型为number
在类型别名、接口、类中使用泛型
直接在名称之后写上<泛型名称>
// 类型别名
// type callback<T> = (n: T, i: number) => boolean
// 接口
interface callback<T> {
(n: T, i: number): boolean
}
// 仿照数组过滤方法
function filter<T>(arr: T[], callback: callback<T>): T[] {
const newArr: T[] = []
arr.forEach((n, i) => {
if (callback(n, i)) {
newArr.push(n)
}
})
return newArr
}
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(filter(arr, n => n % 2 !== 0)); //number
类中的泛型
export class ArrayHelper<T> {
constructor(private arr: T[]) { }
take(n: number): T[] {
if (n >= this.arr.length) {
return this.arr
}
const newArr: T[] = []
for (let i = 0; i < n; i++) {
newArr.push(this.arr[i])
}
return newArr
}
private getRandom(min: number, max: number) {
const dec = max - min
return Math.floor(Math.random() * dec + min)
}
shuffle() {
for (let i = 0; i < this.arr.length; i++) {
const targetIndex = this.getRandom(0, this.arr.length)
const temp = this.arr[i]
this.arr[i] = this.arr[targetIndex]
this.arr[targetIndex] = temp
}
}
}
// 类的泛型
const helper = new ArrayHelper<number>([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) // number
泛型约束
用于限制泛型的取值
// 泛型约束
interface hasNameProperty {
name: string
}
/**
* 将某个对象的name属性的每个单词的首字母大写,然后将该对象返回
*/
function nameToUpperCase<T extends hasNameProperty>(obj: T): T {
obj.name = obj.name.split(' ').map(s => s[0].toUpperCase() + s.substring(1)).join(' ')
return obj
}
const o = {
name: '张三',
age: 23,
gender: '男'
}
const newO = nameToUpperCase(o)
console.log(newO.name);
多泛型
// 将两个数组进行混合 [1,2,3] + ['a','b','c'] = [1,'a',2,'b',3,'c']
function mixinArray<T, K>(arr1: T[], arr2: K[]): (T | K)[] {
if (arr1.length != arr2.length) {
console.log('两个数组长度不一样');
}
let result: (T | K)[] = []
for (let i = 0; i < arr1.length; i++) {
result.push(arr1[i])
result.push(arr2[i])
}
return result
}
const arr1 = [1, 2, 3]
const arr2 = ['a', 'b', 'c']
mixinArray(arr1, arr2) //(string | number)