门户网站模块,网络营销的看法和理解,做非洲国际贸易网站,泰安营销型网站建设文章目录 什么是TS前期准备安装TSTS配置文件 基础类型原始类型 object类型 数组类型 元组类型 枚举 函数类型可选参数和默认参数剩余参数 any任意类型 高级类型交叉类型联合类型 接口类泛型类型别名参考 什么是TS
官网介绍#xff1a;TypeScript是JavaScript类型的超集#… 文章目录 什么是TS前期准备安装TSTS配置文件 基础类型原始类型 object类型 数组类型 元组类型 枚举 函数类型可选参数和默认参数剩余参数 any任意类型 高级类型交叉类型联合类型 接口类泛型类型别名参考 什么是TS
官网介绍TypeScript是JavaScript类型的超集它可以编译成纯JavaScript。可以在任何浏览器、计算机、操作系统上运行并且是开源的。 最终会编译成JavaScript语法在浏览器中运行。 类型系统从可从两个角度来考虑 类型安全可分强类型与弱类型语言 强类型在语言层面上就约束着函数的实参类型必须与形参类型相同另外不允许隐式类型转换。弱类型在语言层面上不要求函数的实参类型与形参类型是相同的允许隐式类型转换。 类型检查可分静态类型与动态类型语言 静态类型一个变量声明时它的类型就是明确的编译阶段中间过程是不允许再修改变量类型了其中java就是典型的静态类型语言。动态类型一个变量在允许阶段才能明确变量的类型而且可以随意修改变量的类型其中js就是典型的动态类型语言。
// 强类型 kotlin
fun sum(a:Int,b: Int){return a b
}
// 在调用时函数实参类型必须形参是一样的
sum(1,2) // 对
sum(1,2) // 错 在调用时就直接报错了不允许隐式类型转换类型不匹配
var c 2 // 声明时Int型
c 2 // 错 不允许修改变量类型// 弱类型 js
funtion sum2(a ,b){return a b
}
// 在调用时函数实参类型可以形参是不一样的
sum(1,2) // 对
sum(1,2) // 虽然调用时不会报错 但和我们预期是不一样的 输出12 是字符串拼接结果 let d 3
d 3 // 对 允许修改变量类型
从类型系统角度看JavaScript是一个弱类型且动态类型语言灵活度是相当的高正因为这种灵活多变的任性 从而丢失了类型系统的安全可靠性在编码时注意要类型的安全性如果在开发环境没有及时发现可能在生产环境导致程序的异常。
JavaScript是没有编译阶段可以直接运行因此具有灵活多变的特性也带来很大的缺陷比如
类型异常只有在运行时才可以发现。类型不明确导致的结果不符合预期。
// 类型异常只有在运行阶段才会被发发现
let obj {}
console.log(obj.name) // undefined// 类型不明确导致的结果不符合预期
function sum(a, b) {return a b
}
console.log(sum(1, 2)) // 输出12 预期是3而TS正好可以弥补JavaScript这种任性在编码时能够确定类型的一致性然后还可以保持着JavaScript的灵活度。强类型的优势
类型安全在编码时能确保类型的一致性让错误在编译时更早的暴露。代码编写更智能更准确提示更友好只会提示对象存在的属性或函数。可以减少类型的判断比如弱语言接收的任意类型需要对类型判断逻辑更多而强类型语言就可以减少不必要的类型判断。重构更安全可靠。
前期准备
安装TS
一、在安装ts之前首先得安装node.js环境。
二、在项目根目录执行npm init -y生成package.json项目配置文件。
三、安装ts
全局安装每个项目都使用同一个版本不建议全局安装不利用项目维护 npm install -g typescript 本地安装在当前项目安装指定版本的ts一般都是在开发环境才使用因为运行时ts会编译成js。 npm install typescript -D 安装成功会在项目根目录下生成一个node_modules目录在bin下有ts的命令符tsc可以用命令tsc -v查看 在IDE安装ts-node可以直接运行ts代码 另外也可以在package.json文件可以看到ts的版本号
{devDependencies: {typescript: ^4.9.3},// .....
}TS配置文件
在根目录执行tsc --init命令生成tsconfig.json配置文件如果根目录执行tsc --init命令无效可以重启项目再次尝试或者进入..\node_modules\.bin再次执行命令 tsc --init这样tsconfig.json就会生成在..\node_modules\.bin目录下我们需要把文件移到项目根目录下。
tsconfig.json部分配置 https://www.tslang.cn/docs/handbook/tsconfig-json.html {compilerOptions: {/*ts编译后所采用的es标准 */target: es2016, /*输出的代码采用模块化方式*/module: commonjs, /*指定源代码的根文件夹*/rootDir: src, sourceMap: true, /*编译后代码输出的位置文件夹*/outDir: dist, }
}如果一个目录下存在一个tsconfig.json文件那么它意味着这个目录是TypeScript项目的根目录 基础类型
原始类型
字符串string数字number数字浮点型、整型、NaN布尔booleanvoid没有任何类型比如函数的没有返回值类型时undefined本身作用不大null本身作用不大
// 基础类型原始类型
// 声明变量或形参的类型具体类型是定义为后置与kotlin是类似的
const a: string foo // 字符串
const b: number 100 // 数字 包括了浮点数
const isDone: boolean true // 布尔值const c: void undefined // void 默认值是 undefined
const d: null null
const e: undefined undefined// Symbol是es2015(es6)标准库中的类型如果target: es5 就会报错
// 如果要使用es6的特性将target配置成es2015
// 或者在不修改target的情况在lib中引入标准库es2015
// 标准库就是内置对象所对应的声明比如Promise、Symbol是ES6标准库中的对象
// 如果修改了tsconfig.json配置运行还是报错说明没有读取到我们修改后的配置文件还是项目内置的配置即最原始配置es5
const s: symbol Symbol(test)console.log(s)其中undefined** 、**null可以是任何类型的子类型也就是可以给类型变量赋值。当然是有提前的如果启动了严格模式或者空检查在编译器中会提示报错如下 在TS配置文件中关闭严格模式strict和空检测strictNullChecks 正常开发时是不会同时关闭strict、strictNullChecks官方建议是开启strictNullChecks如果一个函数的形参接收number或者string、null、undefined类型在严格模式下是不允许number是不允许为null的此时我们可以使用联合类型number | string | null | undefined
// 联合类型
function foo(a: number | string | null | undefined): void {// 字符串模板${..}console.log(foo: ${a})
}
foo(100)
foo(11)
foo(null)
foo(undefined)object类型
// 基础类型Object类型
// 是指非原始类型的其他类型都是object类型比如数组、函数、对象等等// export {} 消除ts文件变量重名的语法报错
export {}let a: object function () {
}
let b: object []
// 约束字面量对象 两边声明和赋值结构必须保持一致
let o: { name: string, age: number } {name: li, age: 12}// 报语法错误
// let x:object 100 数组类型
定义数组类型有两种方式第一种在中括号[]前声明类型第二种数组泛型Arraynumber
// 数组类型
// 有两种方式定义声明第一种在中括号[]前声明类型第二种数组泛型Arraynumber// 第一种声明具体的类型数组
let list: number[] [1, 2, 4]
list.push(22)
// list.push(33) // 报错只能是数字类型// let list [1, 2, ] // 联合类型数组可以存放不同类型元素// 第二种let list2: Arraynumber [1, 2, 3]如果数组不声明具体类型就可以存放任何类型的元素就是一个联合类型数组。 元组类型
元组类型Tuple表示一个已知元素数量和类型的数组各元素的类型不必相同
// 元组表示一个已知元素数量和类型的数组各元素的类型不必相同// [number, string] : 表示已知元素数量和类型的数组初始化值必须对应数量和类型
// 有点类似联合类型
let tuple: [number, string] [12, lili]// 访问元组既然是数组当然是可以通过下标访问
console.log(tuple[0], tuple[1])
// 通过解构也可访问元组
let [age, name] tuple
console.log(age, name)// 元组的类型也可以是空类型可以不初始化值
let tuple2: [number, string?] [12]枚举
在没有枚举的情况JS定义常量通常会用const修饰字面量对象将常量定义成对象属性这样有很强的语义化。
const orderState {Delivered: 1,Received: 2,Returned: 3
}console.log(orderState.Delivered)在TS提供了枚举类型可以很好替换掉字面量常量对象的方式具有更强的可读性。
// 数字枚举默认是从0开始逐个1递增如果第一个设置了初始值会以这个初始值为起点逐个1递增
enum orderState {Delivered 1, Received, Returned
}console.log(orderState)// 字符串枚举要手动赋值enum order {Delivered 已发货, Received 已收货, Returned 退货中
}console.log(order)函数类型
先看看JavaScript函数的弊端
// js
function add(x, y) {return x y
}// js函数的形参可以传任何类型的实参返回值也是不确定的根据形参来变化
// 类型的不确定性会在运行时埋下安全隐患
add(1, true)add()函数可以传入任何的实参类型在编译期是不会提示报错的如果我们期望是求和运算传入实参不是数值在运行时是达不到期望值的因此JavaScript的类型不确定性会给我们的代码在运行时埋下很大的安全隐患。
下面用TypeScript函数来改造
// ts声明形参类型和返回值类型确定了函数类型
// 返回值类型是后置的这点与kotlin是相似的
// 参数类型(x:number,y:number) number
// 箭头左边是参数类型右边是返回值类型如果返回值没有类型则用void表示
function add(x: number, y: number): number {return x y
}// 参数类型 (x: number, y: number) number
let myAdd function (x: number, y: number): number {return x y
}
myAdd(1,200.0)
// myAdd(1,ddd) // 会推断实参类型这里报错// 没有返回值的函数类型(s:string) void
function print(s: string): void {console.log(s)
}
在ts函数中函数是有类型的包括了参数类型和返回值类型两个部分当没有返回值时则用void表示。
函数类型的表达 如上面的myAdd()函数类型是(x:number,y:number) number箭头左边是参数类型右边是返回值类型。 会发现将冒号改成箭头就是一个函数类型。
可选参数和默认参数
除了实参类型要求是一样的在TypeScript里的每个函数参数都是必须的,传递给一个函数的参数个数必须与函数期望的参数个数一致。 在JavaScript中函数传入的参数都是可选的没传参时默认值是undefined 在TypeScript函数实现可选参数有两种方法
可选参数修饰定义默认参数
//------------------- 可选参数和默认参数 ---------------// TypeScript里的每个函数参数都是必须的,传递给一个函数的参数个数必须与函数期望的参数个数一致。// ts也是箭头函数
// let buildName (firstName: string, lastName: string) firstName lastNamefunction buildName(firstName: string, lastName: string) {return firstName lastName
}// 形参的实参类型必须传入两个且类型是要与其一样的。
console.log(buildName(lisi, bob))// 在JavaScript中函数传入的参数都是可选的没传参时默认值是undefined
// 在TypeScript要实现参数是可选的可以在形参名称后面用修饰可选参数必须放在参数后面function buildName2(firstName: string, lastName?: string) {return firstName lastName
}let result2 buildName2(bob)
let result22 buildName2(bob, sr)// 除了在形参名后面修饰声明是可选参数还可以用默认参数实现参数是可选的
// 默认参数不要求放在参数后面
function buildName3(firstName: string, lastName: string sr) {return firstName lastName
}let result33 buildName3(bob)
let result333 buildName3(bob, sr)function buildName4(firstName: string lili, lastName: string sr) {return firstName lastName
}buildName4()
buildName4(lili, bob)
buildName4(undefined, bob)可选参数和默认参数的区别是可选参数修饰必须放在参数的后面而默认参数是没有这个要求的。
默认参数和可选参数的函数本质还是同一个函数类型比如buildName2()和buildName3()函数的类型都是(firstName: string, lastName?:string) string。 剩余参数
如果要函数需要接收多个不确定数量的同类型参数默认参数和可选参数都是不适合的它们只能表示某一个参数在JavaScript里可以通过arguments来访问所有传入的参数。在TypeScript里没有这个arguments但可以通过剩余参数来达到目的。
剩余参数和JavaScript是相似的在参数名称前面添加省略号...用类型数组接收。
function buildName5(firstName: string, ...restName: string[]): string {return firstName restName.join( )
}buildName5(bob, haha, niu)any任意类型
// JavaScript参数是可以接收任何类型的数据由于TypeScript是有类型的
// 如果要兼容JavaScript代码必须要有一个类型来表示任意类型这个类型就是any
// 和kotlin是一样的any是所有类型的父类型。function stringify(obj: any) {return JSON.stringify(obj)
}console.log(stringify([1, 2, 3]))
console.log(stringify({a: 11, b: 22}))
类型断言
// 由于有任意类型any当参数用any接收时如果我们需要是某个具体类型时这个就需要将any断言成具体类型
// 类型断言as并不是将类型强转这与kotlin是不同的// 比如求和函数
function sum(params:any){if (typeof params string){console.log(字符串 params)}if (typeof params number){console.log(数值 params)}// 类型断言let a params as numberconsole.log(a 100)}sum(abc)
sum(22)
高级类型
交叉类型
交叉类型是指两个对象的属性并集合成一个新类型前提是其中一个对象的属性或方法在另外一个对象都具有也就是该类型具有所有类型的特性。
class Student {name: stringage: number
}class Teacher {name: stringage: number_occupation: stringconstructor(occ: string) {this._occupation occ}log() {console.log(occupation: this._occupation)}}// 定义交叉类型使用
let result: Teacher Student;
// result new Student() // 报错 不能赋值Student类型Student没有age、_occupation log
result new Teacher(老师)
result.log()
console.log(result)// 再次赋值
result {name: lili,age: 25,_occupation: 美术老师,log: function () {},// 不能有其他方法或属性报错// test:function (){//// }
}console.log(result)let getUserInfo function (info : Student Teacher){console.log(info.name)console.log(info.age)console.log(info._occupation)info.log()
}getUserInfo(result)// getUserInfo(Student()) // 报错
日志
老师
Teacher { _occupation: 老师 }
{ name: lili, age: 25, _occupation: 美术老师, log: [Function: log] }lili
25
美术老师
Teacher和字面量对象类型具有所有的属性和方法而Student是不满足该条件因此是不能对交叉类型result赋值。
联合类型
联合类型是指两个类型的交集取值可以为多种类型中的一种。 比如定义变量
// 可能是string或者number类型其中一种即可
let myFavoriteNumber: string | number;
myFavoriteNumber seven;
myFavoriteNumber 7;如果联合类型是对象那么只能访问两者公共的属性或方法
//联合类型如果是对象只能访问两者公共的属性或方法class Student {name: string 学生age: number 16}class Teacher {name: string 王老师age: number 25private readonly occupation: stringconstructor(occ: string) {this.occupation occ}log() {console.log(occupation: this.occupation)}}let st : Student | Teacher
st new Student()
console.log(st)
// 虽然是st的实例是指向Teacher对象但只能方法联合类型的共同属性age和name不能访问log()方法
st new Teacher(教美术)
console.log(st)
// st.log() // 报错function getUserInfo(): Student | Teacher {return new Student()
}console.log(getUserInfo()) //只能访问 name 与 age属性function getUserInfo2(): Student | Teacher {return new Teacher(教数学)
}console.log(getUserInfo2())// 访问不到log()方法日志
Student { name: 学生, age: 16 }
Teacher { name: 王老师, age: 25, occupation: 教美术 }
Student { name: 学生, age: 16 }
Teacher { name: 王老师, age: 25, occupation: 教数学 }
与交叉类型不同的是联合类型定义可以使用原始类型而交叉类型不能用原始类型声明。
接口
接口是对行为的抽象具体实现是由类来实现。
一个函数如果有多个形参除了用剩余参数来接收通常还会用对象来接收除此还可以用接口的方式来接收实参可以约束参数的类型。
// 用对象接收多个参数
function test(params: {name: string,age: number,sex: number,occ?: string,
} {name: , age: 0, sex: 0}) {console.log(params)
}test({name: lili, age: 20, sex: 1})// 用接口接收多个参数
interface People {name: string;age: number;readonly sex: number; // 只读属性action?: string;// 可选属性
}function printUser(params: People) {console.log(params)
}// 对象类型要显式声明
let people: People {name: lisi, age: 18, sex: 1}
// people.sex 0 // 只读属性重新赋值会报错
let people2 {name: lili}
printUser(people)
// printUser(people2) // ide会报错缺少属性
当接口显式声明类型时可以用字面量对象给变量赋值
let people: People {name: lisi, age: 18, sex: 1}只读属性关键字readonly 声明可选属性在变量名称加? 字面量对象或class类的属性都是可以这样 readonly sex: number; // 只读属性只能赋值一次通常在创建对象时赋值
action?: string;// 可选属性在Java等面向对象中对象的属性在编译期就已经确定了ts作为前端语言如果不能实现动态化添加属性就失去js的灵活性了因此ts提供了动态属性动态属性又称任意属性。
动态属性用中括号声明[占位符]: 占位符就是声明属性名称是用字符串表示基本是固定的。 interface Animal {name: string;// 动态属性[propName: string]: string;}使用 [propName: string] 定义了动态属性取 string 类型的值
let dog: Animal {name: 狗,}
// 设置动态属性其中 action 和 sex 就是属性名
dog.action 跑
// dog.sex 1 // 与动态属性的值类型与声明类型不匹配会报错
dog.sex 1
console.log(dog)// 访问动态属性
console.log(dog.sex) // 对象.属性名
console.log(dog[sex]) // 对象[属性名] 类似索引的方式
访问动态属性用对象实例.属性名或对象[属性名]
日志
{ name: 狗, action: 跑, sex: 1 }
1
1在接口Animal定义时是没有action 和 sex属性名通常动态属性的设置可以灵活的添加属性。
在一个接口中只能定义一个动态属性如果属性有多个类型的话可以 通过联合类型来定义。比如sex动态属性的值可能是number或string类型使用联合类型定义
interface Animal2 {name: string;[propName: string]: string | number;}let dog2: Animal2 {name : 狗
}dog2.sex 1
dog2.sex 母类
类的概念在面向对象语言中是不可或缺的在es6就已经引进了类的概念。
用class声明一个类constructor定义构造函数通过new创建类的实例。
class People {name: string;// 构造函数constructor(name: string) {this.name name}// 方法不用function定义在es6也是可以省略的getName() {return this.name}}// 创建实例
let p new People(lili)在es6中类的实例属性的作用域是没有约束的默认是公开的如果定义私有属性通常是使用’_下划线来区分外部实例还是可以访问的在ts提供了和java类似的关键字来声明属性的作用域。
默认是publicprivate 私有属性只有在当前类下才可以访问即使是子类也是不能访问的protected 受保护属性只有在类或子类内部才可以访问类外部是无法访问的
class Animal {// 默认是publicname: string;// 在es6 如果定义私有属性通常是使用_下划线来区分外部实例还是可以访问的// 私有属性只有当前类才可以访问即使是子类也是不能访问的// 方法也是一样的private distanceInMeters: number 0// 属性或参数用readonly修饰表示只读不能赋值// private readonly distanceInMeters: number 0// 受保护属性protected _sex: number// 静态属性 与实例是无关的不用实例就可以调用// 每个类都可以访问但内存不是共享的static mouth {size: 5}constructor(name: string, sex: number) {this.name namethis._sex sex}// 方法设置默认参数move(distanceInMeters: number 0) {this.distanceInMeters distanceInMetersconsole.log(${this.name} moved ${distanceInMeters} m)}// 在es6中提供了类的setter和getter方法在java是常见的get sex(): number {return this._sex}set sex(sex: number) {this._sex sex}// 静态方法static getMouth(): string {return Animal.mouth }} 静态属性和方法与类的实例是无关的直接通过类名来调用与java是一样的在内存中是共享的。 继承用extends关键字实现
class Dog extends Animal {// 如果子类不定义构造函数会默认使用父类的构造函数bark() {console.log(woof! woof!)}// 受保护属性: 只有在类或子类内部才可以访问类外部是无法访问的getSex() {return this._sex}
}let dog new Dog(dog, 1)
dog.move(10)
dog.bark()
// dog._sex // 在类外部访问不了protected的属性
console.log(dog sex: dog.getSex())
Animal.mouth {size: 10}
console.log(Animal.mouth)// dog.distanceInMeters // 报错私有属性无法访问
多态是java的三大特性之一在ts同样也是支持的多态的三个必要条件
继承方法的重写父类的引用指向子类
class Snake extends Animal {constructor(name: string, sex: number) {super(name, sex);}// 重写父类的方法move(distanceInMeters: number 5) {super.move(distanceInMeters);}
}class Horse extends Animal {constructor(name: string, sex: number) {super(name, sex);}move(distanceInMeters: number 40) {super.move(distanceInMeters);}
}let snake new Snake(Snake, 1)
// 虽然变量的类型声明是父类Animal但move输出的是子类实例的信息
// 从而也反映了ts的类也是具有多态性
// 多态的三个必要条件1、继承2、重写3、父类引用指向子类实例对象
// 当使用多态方式调用方法时首先检查父类中是否有该方法如果没有则编译错误如果有再去调用子类的同名方法。
let horse: Animal new Horse(Horse, 0)
snake.move() // Snake moved 5 m
horse.move() // Horse moved 40 m
console.log(snake sex: snake.sex) // 访问getter方法// 静态属性与类的实例无关
Snake.mouth {size: 20}
console.log(Snake.mouth)
抽象类不能直接创建实例必须通过子类来创建实例
// 抽象类不能直接创建实例
abstract class Animal2 {// 抽象方法子类必须实现该方法abstract makeSound(): void;move(): void {console.log(roaming the earch...);}
}class Duck extends Animal2 {// 子类实现抽象方法makeSound(): void {}}// 创建实例
let duck new Duck()
接口是行为抽象化没有具体实现需要通过类来实现细节。
interface ClockInterface {currentTime: DatesetTime(d: Date)
}class Clock implements ClockInterface {currentTime: Date;setTime(d: Date) {this.currentTime d}}泛型
泛型是指在定义函数、接口或类的时候不预先指定具体的类型而在使用的时候再指定类型的一种特性。在面向对象语言中是十分重要的优秀的代码都离不开对泛型的理解与使用能将代码模板化提高复用性。
比如下面这个例子在没有使用泛型的情况下
// id形参为了可以接收任意类型声明any类型但返回类型无法确定。
function identity(id: any) {return id
}identity(23)identity(23)使用泛型后在使用期间就知道了具体类型。
// 用泛型就可以解决上面的问题
function identity2T(id: T): T {return id
}
// 指明泛型T的具体类型是number
let id identity2number(12)
console.log(id)泛型也是有类型的比如identity2
// T(id: T) T 就是identity2函数的类型
let myId: T(id: T) T identity2
myIdstring(AABB)泛型在类中的应用
class GenericT {value: T;// 泛型不能做运算// add(x:T,y:T):T{// return x y// }// 定义一个泛型函数类型的属性类型是(x: T, y: T) Tadd: (x: T, y: T) T
}let generic new Genericnumber()
// 没有add变量赋值运行会报错
// generic.add(1,2) // 报错 generic.add is not a function// 赋值后再调用就不会报错
generic.add function (x, y) {return x y
}
let ab generic.add(1, 3)
console.log(ab)//-- 泛型约束
abstract class Animal {abstract getName(): string
}class Dog extends Animal {getName(): string {return Dog;}}class Bee extends Animal {getName(): string {return Bee;}}class Car {getName(): string {return Car;}
}function createAnimalT extends Animal(a: new () T): T {// 在java中泛型是不可以直接在方法中创建实例因此在编译时会被檫出// 在kotlin中 可以通过内联特化和反射创建泛型的实例return new a()
}let d createAnimalDog(Dog)
console.log(d.getName())
let b createAnimal(Bee)
console.log(b.getName())let c createAnimal(Car) // ???
console.log(c.getName())
类型别名
// 类型别名给一个类型取一个新名字
type Name string
type NameResolver () string
type NameOrResolver Name | NameResolver// 函数返回值是一个字符串形参NameOrResolver是一个联合类型是一个函数类型或字符串
function getName(n: NameOrResolver): Name {if (typeof n string) {return n;} else {return n();}
}参考
https://segmentfault.com/a/1190000040220033https://www.tslang.cn/docs/home.htmlhttps://ts.xcatliu.com/introduction/what-is-typescript.htmlhttps://juejin.cn/post/7106079206160891918