npm install -g typescript
使用 tsc ./01.ts 进行编译
vscode 自动编译ts配置
- 第一步
tsc --init 会在目录中生成一个 tsconfig.json文件
- 第二部
终端->执行命令 tsc --watch; 然后编辑的每个ts文件都会转为相同名字的js文件
js和ts类型总览
- JavaScript中的数据类型
string, number, boolean, null, undefined, bigint, symbol, object, 备注:其中object包含:Array、Function、Date、Error等......
- TypeScript中的数据类型
上述javaScript中的所有类型
六个新类型: any、unknown、never、void、tuple、enum
两个用于自定义类型的方式 type、interface
注意点
在JavaScript中的这些内置构造函数:Number、String、Boolean, 它们用于创建对应的包装对象,在日常开发时很少使用,在TypeScript中也是同理,所以在TypeScript中进行类型声明时,通常都要用小写的number、string、boolean
例如下面的代码:
let str1: string
str1 = 'hello'
str1 = new String('hello') //报错
let str2: String
str2 = 'hello'
str2 = new String('hello')
console.log(typeof str1) //string
console.log(typeof str2) //object
- 原始类型VS包装对象
- 原始类型:如number、string、boolean,在JavaScript中是简单数据类型,它们在内存中占用空间少,处理速度快
- 包装对象:如Number对象、String对象、Boolean对象,是复杂类型,在内存中占用更多空间,在日常开始时很少由开发人员自己创建包装对象。
- 自动装箱:JavaScript在必要时会自动将原始类型包装成对象,以便调用方法或访问属性
let str = 'hello';
// 当访问str.length时,JavaScript引擎做了以下工作,
let size = (function () {
// 1.自动装箱:创建一个临时的String对象包装原始字符串
let tempStringObject = new String(str);
// 2.访问String对象的length属性
let lengthValue = tempStringObject.length;
// 3.销毁临时对象,返回长度值
return lengthValue;
})()
console.log(size)
常用类型
- any
从 TypeScript 到 AnyScript 的进化;
any的含义:任意类型,一旦将变量类型限制为any,那就意味着放弃了对该变量的类型检查
- unknown
unknown可以理解为一个类型安全的any, 适用于:不确定数据的具体类型
- never
never的含义是:任何值都不是,简而言之就是不能有值,undefined、null、''、0都不行! never一般是ts主动推断出来的
function demo():never{
throw new Error('程序异常')
}
- void
void 通常用于函数返回值声明,含义:【函数不返回任何值,调用者也不应该依赖其返回值进行任何操作】
- object
关于object与Object, 实际开发中用的相对较少,因为范围太大了。
object(小写)的含义是:所有非原始类型,可存储:对象、函数、数组等,由于限制的范围比较宽泛,在实际开发中使用的相对较少。
let a: object //a能存储的类型是【非原始类型】
a = {}
a = { name: 'tom' }
a = [1, 2, 3, 4]
a = function () { }
a = new String('123')
class Person { }
a = new Person()
// 以下代码,是将【原始类型】赋值给a,有警告
a = 1
a = true
a = '你好'
a = null
a = undefined
let b: Object //b能存储的类型是可以调用到Object方法的类型
b = {}
b = { name: 'tom' }
b = [1, 2, 3, 4]
b = function () { }
b = new String('123')
class Person { }
b = new Person()
b = 1
b = true
b = '你好'
// 以下代码,有警告
b = null
b = undefined
声明对象类型
let person: {
name: string;
age?: number;
[key: string]: any; //索引签名
}
声明函数类型
let count: (a: number, b: number) => number;
count = function (a, b) {
return a + b;
}
声明数组类型
let arr1: string[]
let arr2: Array<string>
arr1 = ['1','2']
arr2 = ['1','2']
- tuple
let arr1:[string,number];
let arr2:[string,boolean?];
let arr3:[number,...string[]];
arr1 = ['hello',21];
arr2 = ['hello'];
arr2 = ['hello',false];
arr3 = [100,'a','b','c'];
arr3 = [100];
- enum
枚举(enum)可以定义一组命名常量,它能增强代码的可读性,也让代码更好维护
// 数字枚举
enum Direction {
Up,
Down,
Left,
Right
}
function walk(data: Direction) {
if (data === Direction.Up) {
console.log('up')
} else if (data === Direction.Down) {
console.log('down')
} else if (data === Direction.Left) {
console.log('left')
} else if (data === Direction.Right) {
console.log('right')
} else {
console.log('unknow')
}
}
walk(Direction.Left)
//字符串枚举
enum Direction {
Up = "Up",
Down = "Down",
Left = "Left",
Right = "Right"
}
console.log(Direction.Down)
- type
type 可以为任意类型创建别名,让代码更简洁,可读性更强,同时更方便地进行类型复用和扩展
//联合类型
type Status = number | string
//交叉类型
type Area = {
height:number;
wight:number;
}
type Address = {
num:number;
cell:number;
room:string;
}
type House = Area & Address
- 一个特殊情况
定义函数的时候直接定义返回值为void
function test():void{
return undefined;
// 以下全部报错
return 1;
return '';
return null;
return []
}
type FunType = () => void;
const test:FunType = ()=>{
// 此处不报错
return 999;
}
// 原因就是为了和以下代码不冲突
const src = [1,2,3]
const dst = [0]
src.forEach(el => dst.push(el))
- 复习类的相关知识
class Person {
name: string
age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
speak() {
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;
}
study(){
console.log(`${this.name}正在学习`)
}
// 重新父类的方法
override speak() {
console.log(`我是学生我叫${this.name}, 今年${this.age}岁`)
}
}
let s1 = new Student('zhangsan',18,'高三');
console.log(s1);
- 属性修饰符
| 修饰符 | 含义 | 具体规则 |
|---|---|---|
| public | 公开的 | 可以被:类内部,子类,类外部 访问 |
| protected | 受保护的 | 可以被:类内部,子类 访问 |
| private | 私有的 | 可以被:类内部 访问 |
| readonly | 只读属性 | 属性无法修改 |
属性的简写形式
class Person {
constructor(public name: string, public age: number) { }
speak() {
console.log(`我叫${this.name}, 今年${this.age}岁`)
}
}
class Student extends Person {
constructor(name: string, age: number, public grade: string) {
super(name, age);
}
study() {
console.log(`${this.name}正在学习`)
}
// 重新父类的方法
override speak() {
console.log(`我是学生我叫${this.name}, 今年${this.age}岁`)
}
}
let s1 = new Student('zhangsan', 18, '高三');
console.log(s1);
readonly修饰符
class Person {
constructor(public name: string, public readonly age: number) { }
speak() {
console.log(`我叫${this.name}, 今年${this.age}岁`)
}
}
- 抽象类
- 概述:抽象类是一种无法被实例化的类,专门用来定义类的机构和行为,类中可以写抽象方法,也可以写具体实现。抽象类主要用来为其派生类提供一个基础结构,要求其派生类必须实现其中的抽象方法
- 简记:抽象类不能实例化,其意义是可以被继承,抽象类里可以有普通方法,也可以有抽象方法。
abstract class Package {
// 构造方法
constructor(public weight: number) { }
// 抽象方法
abstract calculate(): number
// 具体方法
printPackage() {
console.log(`包裹的重量为${this.weight},运费为${this.calculate()}元`)
}
}
class StandardPackage extends Package{
constructor(weight:number,public unitPrice:number){
super(weight);
}
calculate(): number {
return this.weight * this.unitPrice;
}
}
let s1 = new StandardPackage(10,5);
s1.printPackage()
- interface (接口)
interface 是一种定义结构的方式,主要作用为:类、对象、函数等规定一种契约,这样可以确保代码的一致性和类型安全,但要注意interface只能定义格式,不能包含任何实现
- 定义类的结构
interface PersonInterface {
name: string;
age: number;
speak(n: number): void;
}
class Person implements PersonInterface {
constructor(public name: string, public age: number) { }
speak(n: number) {
for (let i = 0; i < n; i++) {
console.log(`你好, 我叫${this.name}, 我的年龄是${this.age}`)
}
}
}
let p1 = new Person('zhangsna',18);
p1.speak(1);
- 定义对象结构
interface UserInterface {
name: string;
readonly gender: string;
age?: number;
run: (n: number) => void
}
const user: UserInterface = {
name: 'zhangsan',
gender: '男',
age: 18,
run: (n) => {
console.log(n)
}
}
- 定义函数结构
interface CountInterface {
(a: number, b: number): number
}
const count: CountInterface = (x, y) => {
return x + y
}
- 接口之间的继承
interface PersonInterface {
name: string;
age: number;
}
interface StudentInterface extends PersonInterface {
grade: string;
}
const stu: StudentInterface = {
name: 'zhangsan',
age: 19,
grade: '四年级'
}
- 接口和抽象类
一个类可以实现多个接口
interface FlyInterface {
fly(): void;
}
interface SwimInterface {
swim(): void;
}
class Duck implements FlyInterface, SwimInterface {
fly(): void {
};
swim(): void {
}
}
一个类只能继承一个抽象类
泛型
泛型允许我们在定义函数、类或接口时,使用类型参数来表示未指定的类型,这些参数在具体使用时,才被指定具体的类型,泛型能让同一段代码适用于多种类型,同时任然保持类型的安全性。
function logData<T>(data: T) {
console.log(data)
}
logData<number>(123);
logData<string>('hello')
function logData<T, U>(data1: T, data2: U): T | U {
return Date.now() % 2 ? data1 : data2
}
logData<number, boolean>(123, true);
logData<string, number>('hello', 456)
泛型接口
interface PersonInterface<T> {
name: string;
age: number,
extraInfo: T
}
type JobInfo = {
title:string;
company:string;
}
let p:PersonInterface<JobInfo> = {
name:'zhangsan',
age:18,
extraInfo:{
title:'高级开发工程师',
company:'发发发科技有限公司'
}
}
泛型类
class Person<T> {
constructor(public name: string, public age: number, public extraInfo: T) { }
speak() {
console.log(this.extraInfo)
}
}
const p1 = new Person<number>('zhangsan', 18, 500);
装饰器
- 装饰器本质是一种特殊的函数,它可以对: 类、属性、方法、参数进行扩展,同时能让代码更简洁。
- 截止目前,装饰器依然是实验性特性,需要开发者手动调整配置,来开启装饰器支持
- 装饰器有5种:
- 类装饰器
- 属性装饰器
- 方法装饰器
- 访问器装饰器
- 参数装饰器
建议使用 "experimentalDecorators": true 配置来开启装饰器支持
类装饰器
1. 基本语法
类装饰器是一个应用在类声明上的函数,可以为类添加额外的功能,或添加额外的逻辑。
function Demo(target: Function) {
console.log('target', target)
}
@Demo
class Person {
constructor(public name: string, public age: number) { }
}
2. 应用举例
需求:定义一个装饰器,实现Person实例调用toString时返回JSON.stringify的执行结果。
function CustomString(target: Function) {
target.prototype.toString = function () {
return JSON.stringify(this)
}
}
@CustomString
class Person {
constructor(public name: string, public age: number) { }
}
const p1 = new Person('zhangsan', 18);
console.log(p1.toString())
3. 关于返回值
类装饰器有返回值:若类装饰器返回一个新的类,那这个新类将替换掉被装饰的类。
类装饰器无返回值:若类装饰器无返回值或返回undefined, 那被装饰的类不会被替换
function Demo(target: Function) {
return class {
test() {
console.log(200)
console.log(300)
console.log(400)
}
}
}
@Demo
class Person {
test() {
console.log(100)
}
}
console.log(Person)
4. 关于构造类型
在TypeScript中,Function类型所表示的范围十分广泛,包括:普通函数、箭头函数、方法等等。但并非Function 类型的函数都可以被new关键字实例化,例如箭头函数是不能被实例化的,那么TypeScript中该如何声明一个构造类型呢?有以下两种方式
- 仅声明构造类型
type Constructor = new (...args:any[]) => {}
function test(fn:Constructor) {}
// const Person = ()=>{}
class Person {
}
test(Person)
- 声明构造类型 + 指定静态属性
type Constructor = {
new(...args: any[]): {},
wife: string
}
function test(fn: Constructor) { }
// const Person = ()=>{}
class Person {
static wife: string
}
test(Person)
5. 替换被装饰的类
对于高级一些的装饰器,不仅仅是覆盖一个原型上的方法,还要有更多功能,例如添加新的方法和状态。
需求:设计一个LogTime装饰器,可以给实例添加一个属性,用于记录实例对象的创建时间,在添加一个方法用于读取创建时间
type Constructor = new (...args: any[]) => {}
function LogTime<T extends Constructor>(target: T) {
return class extends target {
createdTime: Date
constructor(...args: any[]) {
super(...args)
this.createdTime = new Date()
}
getTime() {
return this.createdTime
}
}
}
@LogTime
class Person {
constructor(public name: string, public age: number) { };
speak() {
console.log('你好呀')
}
}
interface Person {
getTime(): void
}
const p1 = new Person('zhangsan', 18);
console.log(p1)
console.log(p1.getTime())
装饰器工厂
装饰器工厂是一个返回装饰器函数的函数,可以为装饰器添加参数,可以更灵活地控制装饰器的行为。
需求:定义一个LogInfo装饰器工厂,实现Person实例可以调用到introduce方法,且introduce中输出内容的次数,由LogInfo接收的参数决定,
type Constructor = new (...args: any[]) => {}
function LogInfo<T extends Constructor>(n: number): Function {
return function (target: T) {
target.prototype.introduce = function () {
for (let i = 0; i < n; i++) {
console.log(`我叫${this.name},我今年${this.age}岁`)
}
}
}
}
@LogInfo(5)
class Person {
constructor(public name: string, public age: number) { };
speak() {
console.log('你好呀')
}
}
interface Person {
introduce(): void
}
const p1 = new Person('zhangsan', 18);
p1.introduce()
装饰器组合
装饰器可以组合使用,执行顺序为:先【由上到下】的执行所有的装饰器工厂,一次获取到装饰器,然后再【由下到上】执行所有的装饰器
装饰器组合 -- 执行顺序
type Constructor = new (...args: any[]) => {}
function test1<T extends Constructor>(target: T) {
console.log('test1')
}
function test2() {
console.log('test2工厂')
return function <T extends Constructor>(target: T) {
console.log('test2')
}
}
function test3() {
console.log('test3工厂')
return function <T extends Constructor>(target: T) {
console.log('test3')
}
}
function test4<T extends Constructor>(target: T) {
console.log('test4')
}
@test1
@test2()
@test3()
@test4
class Person{}
装饰器组合 -- 应用
type Constructor = new (...args: any[]) => {}
interface Person {
introduce(): void,
getTime(): Date
}
// 装饰器
function CustomString<T extends Constructor>(target: T) {
target.prototype.toString = function () {
return JSON.stringify(this)
}
// 封闭原型对象,禁止随意操作原型
Object.seal(target.prototype)
}
// 装饰器工厂
function LogInfo(n: number): Function {
return function <T extends Constructor>(target: T) {
target.prototype.introduce = function () {
for (let i = 0; i < n; i++) {
console.log(`我叫${this.name}, 我今年${this.age}岁了`)
}
}
}
}
// 装饰器
function LogTime<T extends Constructor>(target: T) {
return class extends target {
createdTime: Date;
constructor(...args: any[]) {
super(...args);
this.createdTime = new Date();
}
getTime() {
return this.createdTime
}
}
}
@CustomString
@LogInfo(5)
@LogTime
class Person {
constructor(public name: string, public age: number) { };
speak() {
console.log('你好呀')
}
}
const p1 = new Person('zhangsan', 18);
p1.speak();
console.log(p1.toString());
p1.introduce()
console.log(p1.getTime());
属性装饰器
1. 基本语法
/**
* target: 对于静态属性来说是类,对于实例属性来说值是类的原型对象
* propertyKey:属性名
*/
function Demo(target:object,propertyKey:string){
console.log(target,propertyKey)
}
class Person {
@Demo name: string
@Demo age: number
@Demo static school: string
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
2. 应用举例
需求:定义一个State属性装饰器,来监视属性的修改
/**
* target: 对于静态属性来说是类,对于实例属性来说值是类的原型对象
* propertyKey:属性名
*/
function State(target: object, propertyKey: string) {
let key = `__${propertyKey}`
Object.defineProperty(target, propertyKey, {
get() {
return this[key]
},
set(val) {
this[key] = val
},
enumerable: true,
configurable: true
})
}
class Person {
name: string
@State age: number
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const p1 = new Person('zhangsan', 18);
const p2 = new Person('lisi', 19);
p1.age = 30;
p2.age = 50;
console.log(p1);
console.log(p2);
方法装饰器
基本语法
/**
* target:对于静态方法来说值是类,对于实例方法来说值是原型对象
* propertyKey:方法的名称
* descriptor: 方法的描述对象,其中value属性是被装饰的方法
*
*/
function Demo(target:object,propertyKey:string,descriptor:object){
console.log(target)
console.log(propertyKey)
console.log(descriptor)
}
class Person {
constructor(public name: string, public age: number) { }
@Demo
speak() {
console.log(`我的名字:${this.name}, 我的年龄:${this.age}`)
}
static isAdult(age: number) {
return age >= 18;
}
}
应用举例
需求
- 定义一个Logger方法装饰器,用于在方法执行前和执行后,均追加一些额外逻辑。
- 定义一个Validate方法装饰器,用于验证数据
/**
* target:对于静态方法来说值是类,对于实例方法来说值是原型对象
* propertyKey:方法的名称
* descriptor: 方法的描述对象,其中value属性是被装饰的方法
*
*/
function Logger(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
const originnal = descriptor.value
descriptor.value = function (...args: any[]) {
console.log(`${propertyKey}开始了`)
const result = originnal.call(this, ...args)
console.log(`${propertyKey}结束了`)
return result
}
}
function Validate(maxValue: number) {
return function (target: object, propertyKey: string, descriptor: PropertyDescriptor) {
// 保存原始方法
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
// 自定义验证逻辑
if (args[0] > maxValue) {
throw new Error('年龄非法!')
}
return original.apply(this,args)
}
}
}
class Person {
constructor(public name: string, public age: number) { }
@Logger
speak(str: string) {
console.log(`我的名字:${this.name}, 我的年龄:${this.age},${str}`)
}
@Validate(120)
static isAdult(age: number) {
return age >= 18;
}
}
const p1 = new Person('tom', 18);
p1.speak('123');
let res = Person.isAdult(90)
console.log('res',res)
访问器装饰器
基本语法
/**
* target:对于静态方法来说值是类,对于实例方法来说值是原型对象
* propertyKey:方法的名称
* descriptor: 方法的描述对象,其中value属性是被装饰的方法
*
*/
function Demo(target: object, propertyKey: string, descriptor: PropertyDescriptor) {
console.log(target)
console.log(propertyKey)
console.log(descriptor)
}
class Person {
@Demo
get address() {
return '北京宏福科技园'
}
@Demo
static get country() {
return '中国'
}
}
应用举例
需求 对Weather类的temp属性的set访问器进行限制,设置最低温度 -50,最高温度 50
function RangeValidate(min: number, max: number) {
return function (target: object, propertyKey: string, descriptor: PropertyDescriptor) {
const originalSetter = descriptor.set;
descriptor.set = function (value: number) {
if (value < min || value > max) {
throw new Error(`${propertyKey}的值应该在${min}到${max}之间!`)
}
if (originalSetter) {
originalSetter.call(this, value)
}
}
}
}
class Weather {
private _temp: number;
constructor(_temp: number) {
this._temp = _temp;
}
@RangeValidate(-50, 50)
set temp(value: number) {
this._temp = value
}
get temp() {
return this._temp
}
}
const w1 = new Weather(28);
w1.temp = 40;
console.log(w1.temp)