一、TypeScript
简介:
1、TypeScript
包含了 javaScript
的所有内容。
2、新增了静态类型检查、接口、泛型,更适合大型项目的开发。
3、TypeScript
需要编译为 javaScript
。
二、javaScript
的缺陷:
1、数据类型不明确
2、逻辑有问题,不会报错
3、访问不存在的属性,不会报错
4、低级的拼写错误,不会报错
三、编译 TypeScript
:
全局安装 TypeScript
: npm i typescript -g
1、命令行编译:
使用
tsc
命令编译ts
文件:tsc ts文件名
① 可能会报错:

② 原因:node
版本太低了。
③ 解决办法:
将
node版本
切换到14.0以上
(我用的14.21.3
版本)
④ node版本查询:https://nodejs.p2hp.com/download/
2、自动化编译:
① 生成 tsconfig.json
文件:tsc --init
② 监视某个 ts
文件:tsc --watch ts文件名
③ 监视所有 ts
文件: tsc --watch
当编译出错时,不生成 js
文件
方法一:tsconfig.json
文件修改配置 "noEmitOnError": true,
方法二:执行命令 tsc noEmitOnError --watch
四、TypeScript
类型介绍:
1、包含js
中所有的数据类型:
① string
:
(1)string
和 String
类型的区别:
typescript
let a:string = '小红'
// 报错:不能将类型"String"分配给类型"string"。
// "string"是基元,但"String"是包装器对象。
a = new String("小新")
let b:String = '小米'
b = new String("小兰")
② number
:
(1)number
和 Number
类型的区别:
typescript
let a:number = 333
// 报错:不能将类型"Number"分配给类型"number"。
// "number"是基元,但"Number"是包装器对象。
a = new Number(444)
let b:Number = 111
b = new Number(222)
③ boolean
:
(1)boolean
和 Boolean
类型的区别:
typescript
let a:boolean = true
// 报错:不能将类型"Boolean"分配给类型"boolean"。
// "boolean"是基元,但"Boolean"是包装器对象。
a = new Boolean(false)
let b:Boolean = false
b = new Boolean(true)
④ null
⑤ undefined
⑥ symbol
⑦ bigint
⑧ object
:
包含:
Array
、Function
、Date
、Error
等
(1)object
和 Object
类型的区别:
a、object
能存储【非原始类型】的数据
typescript
let a:object
a = {};
a = { name: '小兰' };
a = [1, 3, 5, 7, 9];
a = function(){};
a = new String('小笼包');
class Person{};
a = new Person();
a = 1; // 报错:不能将类型"number"分配给类型"object"。
a = true; // 报错:不能将类型"boolean"分配给类型"object"。
a = '小红'; // 报错:不能将类型"string"分配给类型"object"。
a = null; // 报错:不能将类型"null"分配给类型"object"。
a = undefined; // 报错:不能将类型"undefined"分配给类型"object"。
b、Object
能存储【可以调用Object
方法】的数据:
除了null
和undefined
之外的所有数据
(2)声明对象类型:
? 表示可选属性
a. 声明对象类型的三种方式:
typescript
// 使用逗号分隔
let person: { name: string, age?:number }
person = {
name: '小红',
age: 18
}
// 使用换行进行分隔
let animal: {
name: string
gender:string
}
animal = {
name: '卡皮巴拉',
gender: '公'
}
// 使用分号进行分隔
let company: { name: string; type:string }
company = {
name: '世大强',
type: '游戏'
}
b. 索引签名:允许定义任意数量的对象属性,适用于定义动态属性
的对象
typescript
let person: {
name: string,
age?:number,
gender: string,
[key:string]: any
}
person = {
name: '小红',
gender: '女',
hobby: '打游戏'
}
(3)声明函数类型:
a、常规声明函数:
typescript
// 声明了一个 有两个参数是 number 类型、返回值也是 number 类型的函数
let func: (a: number,b:number) => number
func = (x,y) => {
return x+y
}
func = function(x,y) {
return x+y
}
b、使用自定义类型
声明函数:
转 3、自定义类型的方式
(4)声明数组类型:
a、常规声明数组:
typescript
let arr:string[]
arr = ['小红', '小明']
b、使用泛型
声明数组:
typescript
let arr:Array<string>
arr = ['小红', '小明']
2、新增的数据类型:
① any
:任意类型
any
类型的变量,可以赋值给任意类型的变量,可以读取变量的任何属性
(1)定义方式一:let 变量名
typescript
let a
a = 11
a = true
a = '小明'
let b:string
b = a // 因为 a最后是string类型,b也是string类型,所以可以赋值成功
// ----------------------分割线---------------------
let c
c = 22
c = '小红'
c = false
let d:string
// 会报错:不能将类型"boolean"分配给类型"string"。
d = c // 因为 c最后是boolean类型,b是string类型,所以不可以赋值成功
(2)定义方式二:let 变量名:any
typescript
let a:any
a = 11
a = '小明'
a = true
a.toUpperCase()
let b:string
b = a
② unknown
:未知类型
(1)unknown
是类型安全的 any
,适用于 不确定数据的具体类型
unknown
类型的变量,不可以赋值给任意类型的变量,也不可以读取变量的任何属性
typescript
let a:unknown
a = 1
a = true
a = '小兰'
let b:string
// 报错:不能将类型"unknown"分配给类型"string"。
b = a
如果想要
unknown
类型的变量,赋值给某类型的变量或者读取某类型变量的属性,可使用如下方式:
a.方式一:做类型检查
typescript
let a:unknown
a = 1
a = true
a = '小兰'
// 报错:"a"的类型为"未知"。
// a.toUpperCase()
// 读取某类型变量的属性方式一:
if (typeof(a) === 'string') {
a.toUpperCase()
}
let b:string
// 赋值给某类型的变量方式一:
if (typeof(a) === 'string') {
b = a
}
b.方式二:断言
typescript
let a:unknown
a = 1
a = true
a = '小兰'
let b:string
// 方式二
b = a as string
c.方式三:断言
typescript
let a:unknown
a = 1
a = true
a = '小兰'
let b:string
// 方式三
b = <string>a
③ never
:不能有值,undefined
、''
、0
、null
都不行!
不是限制变量的,是限制函数的返回类型的
typescript
function test():never {
throw new Error("程序异常")
}
test()
④ void
:限制函数的返回值声明
(1)函数不返回任何值,调用者也不应该依赖其返回值进行任何操作
typescript
function test3():void {
console.log('@')
}
let result = test3()
console.log(result)
// 报错:无法测试 "void" 类型的表达式的真实性。
if(result) { // 依赖返回值进行操作
}
(2) undefined
是 void
可以接受的一种 空
typescript
// 无警告
function test():void {
return undefined
}
// 无警告
function test2():void {
return
}
// 无警告
function test3():void {
}
⑤ tuple
:元组,一种特殊的数组类型,可以存储固定数量的元素。
元组中的每个元素的类型都是已知的,且类型可以不同。
typescript
// ? 表示此类型的元素可选
let arr:[number,string,boolean?]
arr = [1,'小红', true]
// ... 表示可以有0-n个此类型的元素
let arr2:[number,string,boolean?, ...string[]]
arr2 = [1,'小红', true, '小花', '小明']
⑥ enum
:枚举,定义一组命名常量
可以增强代码的可读性,也可以让代码更好维护。
(1)数字枚举:
typescript
enum AirMode {
cool,
hot,
dry,
auto
}
console.log(AirMode) // {0: 'cool', 1: 'hot', 2: 'dry', 3: 'auto', cool: 0, hot: 1, dry: 2, auto: 3}
function getMode(data:AirMode) {
if (data === AirMode.cool) {
console.log('制冷模式')
} else if (data === AirMode.hot) {
console.log('制热模式')
} else if (data === AirMode.dry) {
console.log('除湿模式')
} else if (data === AirMode.auto) {
console.log('自动模式')
} else {
console.log('暂无模式')
}
}
getMode(AirMode.auto)
console.log(AirMode[0]) // cool
可以自定义递增的数字:
typescript
enum AirMode {
cool = 2,
hot,
dry,
auto
}
console.log(AirMode) // {2: 'cool', 3: 'hot', 4: 'dry', 5: 'auto', cool: 2, hot: 3, dry: 4, auto: 5}
(2)字符串枚举:
会丢失反向映射
typescript
enum AirMode {
Cool = "cool",
Hot = "hot",
Dry = "dry",
Auto = "auto"
}
console.log(AirMode) // {Cool: 'cool', Hot: 'hot', Dry: 'dry', Auto: 'auto'}
(3)常量枚举:使用const
关键字定义,在编译时会被 内联
所谓"内联"其实就是
TypeScript
在编译时,会将枚举成员引用
替换为它们的实际值
,而不是生成额外的枚举对象。
内联的好处:减少生成的 JavaScript 代码量,并提高运行时性能。
普通枚举:
常量枚举:
3、自定义类型的方式:
① type
:为任意类型创建别名
(1)基本用法:
a、定义对象
typescript
// 定义 Box 类型
type Box = {
width: number,
height: number
}
let pinkBox:Box = {
width: 20,
height: 30
}
b、定义类
typescript
type Box = {
width: number,
height: number
}
class area implements Box {
constructor(
public width:number,
public height: number
){
}
calc(color: string) {
console.log(`我有一个${color}房子,面积是${this.width * this.height}平方米。`)
}
}
let a1 = new area(12, 10)
a1.calc('红') // 我有一个红房子,面积是120平方米。
c、定义函数
typescript
type func = (width: number,height: number) => void
let a1:func = (width: number,height: number) => {
console.log(`面积是${width*height}平方米。`)
}
a1(12,10) // 面积是120平方米。
(2)联合类型:|
一个值的类型可以是多个类型中的一个
typescript
type key = string | number
let a:key = 111
type Gender = '男' | '女'
let getGender:(value:Gender) => Gender
getGender = (value) => {
console.log(value)
return value
}
getGender('女')
(3)交叉类型:&
一个值的类型是由多个类型合并得出的。
常用于对象类型
typescript
type Size = {
width: number,
height: number
}
type Style = {
color: string,
border: number,
radius: number
}
let box: Size & Style = {
width: 200,
height: 400,
color: 'pink',
border: 2,
radius: 8,
}
(4)特殊情况:void
使用类型声明
限制函数返回值为 void
时,TypeScript
并不会严格要求返回值为空。
官网链接:https://www.typescriptlang.org/docs/handbook/2/functions.html#assignability-of-functions
typescript
type specialFunc = () => void
let func1:specialFunc = () => {
// 允许返回非空类型
return 123
}
let res = func1()
// 报错:无法测试 "void" 类型的表达式的真实性。
// if (res) {}
② interface
:接口
请移步七、接口
五、属性修饰符:
1、public
:公开的
① 被 类内部
访问:
typescript
class Person {
public name: string;
public age: number;
constructor(name:string, age: number) {
this.name = name
this.age = age
}
public say() {
// 在类的内部使用
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
② 被 子类
访问(内部外部皆可):
typescript
class Person {
public name: string;
public age: number;
constructor(name:string, age: number) {
this.name = name
this.age = age
}
public say() {
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
// 在子类使用
class Student extends Person {
grade: string;
constructor(name: string,age: number, grade: string) {
super(name,age)
this.grade = grade
}
override say() {
// 在子类内部访问
console.log(`我的名字是${this.name},我今年${this.age}岁了,我在${this.grade}年级学习。`)
}
}
// 在子类外部访问
let s1 = new Student('小蓝', 10, '二')
console.log(s1.name) // 小蓝
console.log(s1.age) // 10
s1.say() // 我的名字是小蓝,我今年10岁了,我在二年级学习。
③ 被 类外部
访问:
typescript
class Person {
public name: string;
public age: number;
constructor(name:string, age: number) {
this.name = name
this.age = age
}
public say() {
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
// 在类的外部使用
let p1 = new Person('小红', 18)
console.log(p1.name) // 小红
console.log(p1.age) // 18
s1.say() // 我的名字是小红,我今年18岁了。
2、属性的简写形式:
typescript
class Person {
constructor(public name:string, public age: number) {}
public say() {
// 在类的内部使用
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
3、protected
:受保护的
① 被 类内部
访问:
typescript
class Person {
constructor(
protected name:string,
protected age: number
) {}
protected say() {
// 在类内部使用
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
② 被 子类内部
访问:
typescript
class Person {
constructor(
protected name:string,
protected age: number
) {}
protected say() {
// 在类内部使用
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
// 在子类使用
class Student extends Person {
constructor(
name: string,
age: number,
public grade: string
) {
super(name,age)
}
override say() {
console.log(`我的名字是${this.name},我今年${this.age}岁了,我在${this.grade}年级学习。`)
}
}
let s1 = new Student('小蓝', 10, '二')
// console.log(s1.name) // 报错 -- 属性"name"受保护,只能在类"Person"及其子类中访问。
s1.say() // 我的名字是小蓝,我今年10岁了,我在二年级学习。
4、private
:私有的
① 被 类内部
访问:
typescript
class Person {
constructor(private name:string, private age: number) { }
private say() {
// 在类内部使用
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
class Student extends Person {
constructor(
name: string,
age: number,
public grade: string
) {
super(name,age)
}
private speak() {
// 报错:属性"name"为私有属性,只能在类"Person"中访问。
// console.log(`我的名字是${this.name},我今年${this.age}岁了,我在${this.grade}年级学习。`)
}
}
let s1 = new Student('小蓝', 10, '二')
console.log(s1) // Student {name: '小蓝', age: 10, grade: '二'}
// console.log(s1.name) // 报错 -- 属性"name"为私有属性,只能在类"Person"中访问。
5、readonly
:只读属性,属性无法修改
typescript
class Person {
constructor(public readonly name:string, public age: number) {}
public say() {
// 在类的内部使用
console.log(`我的名字是${this.name},我今年${this.age}岁了。`)
}
}
let p1 = new Person('小红', 18)
console.log(p1) // Person {name: '小红', age: 18}
// p1.name = '小兰' // 报错:无法为"name"赋值,因为它是只读属性。
p1.age = 20
console.log(p1) /// Person {name: '小红', age: 20}
六、抽象类:
1、定义:
① 抽象类无法被实例化:
不能用
new
关键字去调用,可以被继承
② 抽象类专门用来定义类的结构和行为:
定义通用的属性和方法
③ 抽象类中可以写抽象方法:
定义抽象方法的名字、入参类型和返回值类型
④ 抽象类中可以写具体的实现:
定义具体方法,在具体方法中使用 抽象方法中的属性 / 抽象方法 来计算或者输出数据
⑤ 为派生类(子类)提供一个基础结构:
子类共享抽象类中的部分代码
⑥ 派生类(子类)必须实现抽象类的抽象方法:
子类必须定义一个和抽象类中抽象方法同名的方法,并且要定义该方法的返回值
2、使用场景:
① 定义通用接口:
为一组相关的类定义通用的行为(方法或属性)时。
② 提供基础实现:
在抽象类中提供某些方法或为其提供基础实现,这样派生类就可以继承这些实现。
③ 确保关键实现:
强制派生类实现一些关键行为。
④ 共享代码和逻辑:
当多个类需要共享部分代码时,抽象类可以避免代码重复。
3、具体实现:
typescript
abstract class Person {
// 构造方法
constructor(public name:string, public age: number) { }
// 抽象方法 --关键实现
abstract hobby(): string
// 具体方法 -- 通用接口、基础实现
speak() {
console.log(`我叫${this.name},我喜欢玩${this.hobby()}`)
}
}
class Player extends Person {
constructor(name:string, age: number, private hobbyName:string) {
super(name, age)
}
hobby():string {
if (this.age > 18) {
return this.hobbyName
} else {
return `${this.hobbyName},但是我也要努力学习。`
}
}
}
let p1 = new Player('小红', 20, '《魔兽世界》')
p1.speak() // 我叫小红,我喜欢玩《魔兽世界》
let p2 = new Player('小兰', 15, '《蛋仔派对》')
p2.speak() // 我叫小兰,我喜欢玩《蛋仔派对》,但是我也要努力学习。
七、接口:interface
1、解释:
interface
主要用来定义类
、对象
、函数
的结构,不能包含实现!
2、优点:
可以保证代码的一致性和类型安全
3、使用:
① 定义类
结构:
使用 implements
实现接口
typescript
// 定义接口
interface PersonInterface {
name:string;
age: number;
introduce: (grade:string) => void
}
// 类 实现 接口
class Student implements PersonInterface {
constructor(
public name: string,
public age: number
) {
}
// introduce(grade: string) {
// console.log(`我的名字是${this.name},我今年${this.age}岁,上${grade}年级。`)
// }
introduce = (grade: string) => {
console.log(`我的名字是${this.name},我今年${this.age}岁,上${grade}年级。`)
}
}
let s1 = new Student('小红', 8)
s1.introduce('二') // 我的名字是小红,我今年8岁,上二年级。
② 定义对象
结构:
typescript
// 定义接口
interface PersonInterface {
name:string;
age: number;
introduce: (grade:string) => void
}
let student: PersonInterface = { // 对象的类型是某个接口
name: '小红',
age: 8,
introduce: (grade:string) => {
console.log(`我的名字是${student.name},我今年${student.age}岁,上${grade}年级。`)
}
}
student.introduce('二') // 我的名字是小红,我今年8岁,上二年级。
③ 定义函数
结构:
typescript
// 定义 PersonInterface接口
interface PersonInterface {
(name:string, age: number) : void
}
let student:PersonInterface = (name:string, age: number) => {
console.log(`我的名字是${name},我今年${age}岁。`)
}
student('小红', 15) // 我的名字是小红,我今年15岁。
4、接口之间的继承:
接口继承后,最终定义的数据需要满足所有接口的结构
typescript
// 定义PersonInterface接口
interface PersonInterface {
name:string;
age: number;
introduce: (grade:string) => void
}
// HobbyInterface接口 继承 PersonInterface接口
interface HobbyInterface extends PersonInterface{
hobby: string;
gender?:string;
}
// 需要满足两个接口的内容
let student: HobbyInterface = {
name: '小红',
age: 8,
hobby: '读书',
introduce: (grade:string) => {
console.log(`我的名字是${student.name},我今年${student.age}岁,上${grade}年级,喜欢${student.hobby}。`)
}
}
student.introduce('二') // 我的名字是小红,我今年8岁,上二年级,喜欢读书。
5、接口自动合并:
等同于type
的交叉类型,但是比交叉类型灵活
可以为同一个接口,追加不同的结构
typescript
// 定义PersonInterface接口
interface PersonInterface {
name:string;
age: number;
introduce: (grade:string) => void
}
// 给 PersonInterface接口 新增结构
interface PersonInterface{
hobby: string;
gender?:string;
}
let student: PersonInterface = {
name: '小红',
age: 8,
hobby: '读书',
introduce: (grade:string) => {
console.log(`我的名字是${student.name},我今年${student.age}岁,上${grade}年级,喜欢${student.hobby}。`)
}
}
student.introduce('二') // 我的名字是小红,我今年8岁,上二年级,喜欢读书。
6、使用场景:
① 定义对象的格式:
数据模型、API响应格式、配置对象
② 类的契约:
规定一个类需要实现的属性和方法
③ 自动合并:
扩展第三方库的类型
八、泛型:
1、定义:
在定义函数、类、接口的时候,使用
类型参数
表示未指定的类型
,需要等到使用的时候才会被指定具体的类型
。
2、优点:
可以让同一段代码适用多个类型,同时保持类型的安全性。
3、使用:
① 泛型函数:
定义函数:入参要使用泛型
<T>
,那么函数必须要指定<T>
。使用函数:函数要指定入参的类型
typescript
// 泛型的字母T可以随意更改
function func<T> (a:T) {
console.log(a)
}
func<number>(111)
func<string>('哈哈')
② 指定多个泛型:
typescript
function func<T,H> (a:T, b:H, c:number) : T | H {
return c % 2 ? a : b
}
type Box = {
width: number;
height: number;
}
let res = func<number, Box>(123, {
width: 1,
height: 3
}, 3)
console.log(res) // 123
③ 泛型接口:
定义:属性要使用泛型
<T>
,那么接口必须要指定<T>
。使用:接口要指定入参的类型
typescript
interface PersonInterface<T> {
name: string;
age: number;
ExtroInfo: T;
}
let p1:PersonInterface<number> = {
name: '小红',
age: 18,
ExtroInfo: 20
}
let p2:PersonInterface<string> = {
name: '小红',
age: 18,
ExtroInfo: '二年级'
}
④ 泛型约束:
使用:在指定泛型的时候,也要对泛型本身进行约束
typescript
interface PersonInterface {
name: string;
age: number;
}
function func<T extends PersonInterface>(a: T): void {
console.log(`我叫${a.name},今年${a.age}岁`)
}
func({name: '小红', age: 18}) // 我叫小红,今年18岁
⑤ 泛型类:
typescript
class Person<T> {
constructor(
public name:string,
public age: number,
public info: T
){}
say() {
console.log(`我的名字是${this.name}, 年龄是${this.age}。`)
console.log(this.info)
}
}
let p1 = new Person<string>('小红', 18, '哈哈哈')
p1.say() // 我的名字是小红, 年龄是18。 哈哈哈
let p2 = new Person<number>('小兰', 8, 666)
p2.say() // 我的名字是小兰, 年龄是8。 666
type Student = {
gender: string;
title: string
}
let p3 = new Person<Student>('小李', 28, {
gender: '男',
title: '自我介绍'
})
p3.say() // 我的名字是小李, 年龄是28。 {gender: '男', title: '自我介绍'}
九、类型声明文件:xx.d.ts
在
ts
中使用js
文件时,一般会有xx.d.ts
文件来 为js
文件提供类型信息,使得ts
可以对js
文件进行类型检查和提示。
typescript
// xx.d.ts 文件内容示例:
declare const count: number;
// ...
十、装饰器:
文章链接:https://blog.csdn.net/Y1914960928/article/details/146209433