初识TypeScript
TypeScript 的介绍
TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。
TypeScript扩展了JavaScript的语法,所以任何现有的JavaScript程序可以运行在TypeScript环境中。
TypeScript是为大型应用的开发而设计,并且可以编译为JavaScript。
TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6+ 的支持**,它由 Microsoft 开发,代码开源于 GitHub 上
TypeScript 主要有3 大特点
始于JavaScript,归于JavaScript
TypeScript 可以编译出纯净、 简洁的 JavaScript 代码,并且可以运行在任何浏览器上、Node.js 环境中和任何支持 ECMAScript 3(或更高版本)的JavaScript 引擎中。
强大的类型系统
类型系统允许 JavaScript 开发者在开发 JavaScript 应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。
先进的 JavaScript
TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件。
总结
TypeScript 在社区的流行度越来越高,它非常适用于一些大型项目,也非常适用于一些基础库,极大地帮助我们提升了开发效率和体验。
安装 TypeScript
命令行运行如下命令,全局安装 TypeScript:
npm install -g typescript
安装完成后,在控制台运行如下命令,检查安装是否成功(3.x):
tsc -V
第一个 TypeScript 程序
编写 TS 程序
src/hellowrld.ts
function greeter(person) {
return'Hello,'+person
}
let user = 'Yee'
console.log(greeter(user))
手动编译代码
我们使用了 .ts 扩展名,但是这段代码仅仅是 JavaScript 而已。
在命令行上(终端),运行 TypeScript 编译器:
tsc hellworld.ts
输出结果为一个helloword.js文件。包含了和输入文件相同的JavaScript代码
在命令行上,通过 Node.js 运行这段代码:
node helloworld.js
控制台输出:
Hello, Yee
vscode自动编译
1). 生成配置文件tsconfig.json
tsc --init
2). 修改tsconfig.json配置
"outDir": "./js",
"strict": false,
3). 启动监视任务:
终端 -> 运行任务 -> 监视tsconfig.json
类型注解
TypeScript 工具带来的高级功能。 给 person 函数的参数添加 : string 类型注解,如下
function greeter (person: string) {
return 'Hello, ' + person
}
let user = 'Yee'
console.log(greeter(user))
TypeScript 里的类型注解是一种轻量级的为函数或变量添加约束的方式。 在这个例子里,我们希望 greeter 函数接收一个字符串参数。 然后尝试把 greeter 的调用改成传入一个数组
function greeter (person: string) {
return 'Hello, ' + person
}
let user = [0, 1, 2]
console.log(greeter(user))
重新编译,你会看到产生了一个错误:
error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'string'.
类似地,尝试删除 greeter 调用的所有参数。 TypeScript 会告诉你使用了非期望个数的参数调用了这个函数。 在这两种情况中,TypeScript提供了静态的代码分析,它可以分析代码结构和提供的类型注解。
要注意的是尽管有错误,greeter.js 文件还是被创建了。 就算你的代码里有错误,你仍然可以使用 TypeScript。但在这种情况下,TypeScript 会警告你代码可能不会按预期执行。
接口
让我们继续扩展这个示例应用。这里我们使用接口来描述一个拥有 firstName 和 lastName 字段的对象。 在 TypeScript 里,只在两个类型内部的结构兼容,那么这两个类型就是兼容的。 这就允许我们在实现接口时候只要保证包含了接口要求的结构就可以,而不必明确地使用 implements 语句。
interface Person {
firstName:sting
lastName:string
}
function greeter(person:Person) {
return 'Hello,'+person.firstName+' 'person.lastname
}
let user = {
flastName:'Yee',
lastName:'Huang'
}
console.log(greeter(user))
类
最后,让我们使用类来改写这个例子。 TypeScript 支持 JavaScript 的新特性,比如支持基于类的面向对象编程。
让我们创建一个 User 类,它带有一个构造函数和一些公共字段。因为类的字段包含了接口所需要的字段,所以他们能很好的兼容。
还要注意的是,我在类的声明上会注明所有的成员变量,这样比较一目了然。
class USer {
fullName:string
fistName:string
lastName:string
constructor(firstName:string,lastName:string) {
this.firstName = firstName
this.lastName = lastName
this.fullName = firstName+' '+lastName
}
}
interface Person {
firstName:string
lastName:string
}
function greeter (person:Person) {
return 'Hello,+ person.firstName + '' + person.lastName
}
let user = new User('Yee','Huang')
console.log(greeter(user))
重新运行 tsc greeter.ts 你会看到TypeScript里的类只是一个语法糖,本质还是JavaScript实现
使用webpack打包TS
下载依赖
npm install -D typescript
npm install -D webpack webpack-cli
npm install -D webpack-dev-server
npm install -D html-webpack-plugin clean-webpack-plugin
npm install -D ts-loader
npm install cross-env
安装依赖出现版本过高问题,参考下面链接
npm install -D html-webpack-plugin@4.5.0
npm install -D ts-loader@4.4.2
入口JS: src/main.ts
// import './01_helloworld'
document.write('Hello Webpack TS!')
index页面: public/index.html
<!DOCTYPE html>
<htmllang="en">
<head>
<metacharset="UTF-8">
<metaname="viewport"content="width=device-width, initial-scale=1.0">
<metahttp-equiv="X-UA-Compatible"content="ie=edge">
<title>webpack&TS</title>
</head>
<body>
</body>
</html>
build/webpack.config.js
const{CleanWebpackPlugin}=require('clean-webpack-plugin')
constHtmlWebpackPlugin=require('html-webpack-plugin')
constpath=require('path')
constisProd=process.env.NODE_ENV==='production'// 是否生产环境
functionresolve(dir) {
returnpath.resolve(__dirname,'..',dir)
}
module.exports={
mode:isProd?'production':'development',
entry: {
app:'./src/main.ts'
},
output: {
path:resolve('dist'),
filename:'[name].[contenthash:8].js'
},
module: {
rules: [
{
test:/\.tsx?$/,
use:'ts-loader',
include: [resolve('src')]
}
]
},
plugins: [
newCleanWebpackPlugin({
}),
newHtmlWebpackPlugin({
template:'./public/index.html'
})
],
resolve: {
extensions: ['.ts','.tsx','.js']
},
devtool:isProd?'cheap-module-source-map':'cheap-module-eval-source-map',
devServer: {
host:'localhost',// 主机名
stats:'errors-only',// 打包日志输出输出错误信息
port:8081,
open:true
},
}
配置打包命令
"dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js",
"build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js"
运行与打包
yarn dev
yarn build
Typescript常用语法
基础类型
布尔值
最基本的数据类型就是简单的 true/false 值,在JavaScript 和 TypeScript 里叫做 boolean(其它语言中也一样)。
// : 类型
letisDone:boolean=false;
isDone=true;
数字
和 JavaScript 一样,TypeScript 里的所有数字都是浮点数。 这些浮点数的类型是 number。 除了支持十进制和十六进制字面量,TypeScript 还支持 ECMAScript 2015中引入的二进制和八进制字面量。
leta1:number=10// 十进制
leta2:number=0b1010// 二进制
leta3:number=0o12// 八进制
leta4:number=0xa// 十六进制
字符串
JavaScript 程序的另一项基本操作是处理网页或服务器端的文本数据。 像其它语言里一样,我们使用 string 表示文本数据类型。 和 JavaScript 一样,可以使用双引号(")或单引号(')表示字符串。
letname:string='tom'
name='jack'
// name = 12 // error
letage:number=12
constinfo=`My name is ${name}, I am ${age}years old!`
总结:ts中变量一开始是什么类型,那么后期赋值的时候就只能用这个类型,不允许用其他类型的数据赋值给这个变量如
let str:string = '你好'//可以
let str:string = 1234 //error
undefined和null
TypeScript 里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。 它们的本身的类型用处不是很大:
letu:undefined=undefined
letn:null=null
默认情况下 null 和 undefined 是所有类型的子类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。
//需要将tsconfig.json 中的严格模式关闭
"strict": false, //是否开启严格模式
数组
TypeScript 像 JavaScript 一样可以操作数组元素。 有两种方式可以定义数组。 第一种,可以在元素类型后面接上[],表示由此类型元素组成的一个数组:
//语法" let 变量名:数据类型[] = [值1,值2,值3]
letlist1:number[]=[1,2,3]
第二种方式是使用数组泛型,Array<元素类型>:
//语法:let 变量名:Array<数据类型> = [值1,值2,值3]
letlist2:Array<number>=[1,2,3]
数组定义后,里面的数据类型必须和定义数组时候的类型是一致的,否则error
元组 Tuple
元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string 和 number 类型的元组。
//在定义数组的时候,类型和数据的个数一开始就已经限定了
lett1: [string,number]
t1=['hello',10]// OK
t1=[10,'hello']// Error
当访问一个已知索引的元素,会得到正确的类型:
console.log(t1[0].substring(1))// OK
console.log(t1[1].substring(1))// Error, 'number' 不存在 'substring' 方法
枚举
enum 类型是对 JavaScript 标准数据类型的一个补充。 使用枚举类型可以为一组数值赋予友好的名字。
enumColor{
Red,
Green,
Blue
}
// 枚举数值默认从0开始依次递增
// 根据特定的名称得到对应的枚举数值
letmyColor:Color=Color.Green
console.log(myColor,Color.Red,Color.Blue)// 1 0 2
默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将上面的例子改成从 1 开始编号:
enumColor{Red=1,Green,Blue}
letc:Color=Color.Green
或者,全部都采用手动赋值:
enumColor{Red=1,Green=2,Blue=4}
letc:Color=Color.Green
枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:
enumColor{Red=1,Green,Blue}
letcolorName:string=Color[2]
console.log(colorName)// 'Green'
any
有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any 类型来标记这些变量:
letnotSure:any=4
notSure='maybe a string'
notSure=false// 也可以是个 boolean
在对现有代码进行改写的时候,any 类型是十分有用的,它允许你在编译时可选择地包含或移除类型检查。并且当你只知道一部分数据的类型时,any 类型也是有用的。 比如,你有一个数组,它包含了不同的类型的数据:
letlist:any[]=[1,true,'free']
list[1]=100
void
某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void:
/* 表示没有任何类型, 一般用来说明函数的返回值不能是undefined和null之外的值 */
functionfn():void{
console.log('fn()')
// return undefined
// return null
// return 1 // error
}
声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefined 和 null:
letunusable:void=undefined
object
object 表示非原始类型,也就是除 number,string,boolean之外的类型。
使用 object 类型,就可以更好的表示像 Object.create 这样的 API。例如:
functionfn2(obj:object):object{// 定义的参数是object类型 返回值也是object类型
console.log('fn2()',obj)
return{}
// return undefined
// return null
}
console.log(fn2(newString('abc')))
// console.log(fn2('abc') // error
console.log(fn2(String))
联合类型
联合类型(Union Types)表示取值可以为多种类型中的一种需求1: 定义一个一个函数得到一个数字或字符串值的字符串形式值
functiontoString2(x:number|string) :string{
returnx.toString()
}
需求2: 定义一个一个函数得到一个数字或字符串值的长度
functiongetLength(x:number|string) {
// return x.length // error
if(x.length) {// error
returnx.length
}else{
returnx.toString().length
}
}
类型断言
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式。 其一是“尖括号”语法, 另一个为 as 语法
/*
类型断言(Type Assertion): 可以用来手动指定一个值的类型
语法:
方式一: <类型>值
方式二: 值 as 类型 tsx中只能用这种方式
*/
/* 需求: 定义一个函数得到一个字符串或者数值数据的长度 */
functiongetLength(x:number|string) {
if((<string>x).length) {
return(xasstring).length
}else{
returnx.toString().length
}
}
console.log(getLength('abcd'),getLength(1234))
类型推断
类型推断: TS会在没有明确的指定类型的时候推测出一个类型有下面2种情况: 1. 定义变量时赋值了, 推断为对应的类型. 2. 定义变量时没有赋值, 推断为any类型
/* 定义变量时赋值了, 推断为对应的类型 */
letb9=123// number
// b9 = 'abc' // error
/* 定义变量时没有赋值, 推断为any类型 */
letb10// any类型
b10=123
b10='abc'
接口
接口初探
需求: 创建人的对象, 需要对人的属性进行一定的约束
id是number类型, 必须有, 只读的
name是string类型, 必须有
age是number类型, 必须有
sex是string类型, 可以没有
下面通过一个简单示例来观察接口是如何工作的:
/*
在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型
接口: 是对象的状态(属性)和行为(方法)的抽象(描述)
接口类型的对象
多了或者少了属性是不允许的
可选属性: ?
只读属性: readonly
*/
/*
需求: 创建人的对象, 需要对人的属性进行一定的约束
id是number类型, 必须有, 只读的
name是string类型, 必须有
age是number类型, 必须有
sex是string类型, 可以没有
*/
// 定义人的接口
interfaceIPerson{
id:number
name:string
age:number
sex:string
}
constperson1:IPerson={
id:1,
name:'tom',
age:20,
sex:'男'
}
类型检查器会查看对象内部的属性是否与IPerson接口描述一致, 如果不一致就会提示类型错误。
属性
接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。
interfaceIPerson{
id:number
name:string
age:number
sex?:string
}
带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号。
可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误。
constperson2:IPerson={
id:1,
name:'tom',
age:20,
// sex: '男' // 可以没有
}
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性:
interfaceIPerson{
readonlyid:number
name:string
age:number
sex?:string
}
一旦赋值后再也不能被改变了。
constperson2:IPerson={
id:2,
name:'tom',
age:20,
// sex: '男' // 可以没有
// xxx: 12 // error 没有在接口中定义, 不能有
}
person2.id=2// error
readonly vs const
最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly。
函数类型
接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
/*
接口可以描述函数类型(参数的类型与返回的类型)
*/
interfaceSearchFunc{
(source:string,subString:string):boolean
//参数的类型 //返回的值
}
这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。
constmySearch:SearchFunc=function(source:string,sub:string):boolean{
returnsource.search(sub)>-1
}
console.log(mySearch('abcd','bc'))
类类型
类实现接口
与 C# 或 Java 里接口的基本作用一样,TypeScript 也能够用它来明确的强制一个类去符合某种契约。
/*
类类型: 实现接口
1. 一个类可以实现多个接口
2. 一个接口可以继承多个接口
*/
interfaceAlarm{
alert():any;
}
interfaceLight{
lightOn():void;
lightOff():void;
}
classCarimplementsAlarm{
alert() {
console.log('Car alert');
}
}
一个类可以实现多个接口
classCar2implementsAlarm,Light{
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
接口继承接口
和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。
interfaceLightableAlarmextendsAlarm,Light{
}
类
基本示例
下面看一个使用类的例子:
/*
类的基本定义与使用
*/
classGreeter{
// 声明属性
message:string
// 构造方法
constructor(message:string) {
this.message=message
}
// 一般方法
greet():string{
return'Hello '+this.message
}
}
// 创建类的实例
constgreeter=newGreeter('world')
// 调用实例的方法
console.log(greeter.greet())
// 1.类 类型:类的类型,类的类型,类的类型可以通过接口来实现
(()=>{
// 定义一个接口
interfaceIFly{
fly()
}
//定义一个类,这个类就是上面定义的接口,也可以理解为,IFly接口约束了当前的这个Person类
classPersonimplementsIFly{//implement实现 person 实现 IFly接口
//实现接口的方法
fly() {
console.log('我会飞了,我是超人')
}
}
// 实例化对象
constperson=newPerson()
person.fly()
//--------------------------------
//2.定义一个接口
interfaceISwim{
swim()
}
// 定义一个类,这个类就是IFly和ISwim(当前这个类可以实现多个接口,一个类同时也可以被多个接口进行约束)
classPerson2implementsIFly,ISwim{
fly(){
console.log('我飞了2')
}
swim() {
console.log('我游泳2')
}
}
//实例化对象
constperson2=newPerson2
person2.fly()
person2.swim()
// 类可以通过接口的方式来定义这个类的类型
// 类可以实现一个接口,也可以实现多个接口。要注意接口中内容都要真正的是实现
//--------------------------------
//3.定义了一个接口,可以继承其他的多个接口
interfaceIMyFlyAndSwimextendsIFly,ISwim{ }
//定义一个类,直接实现IMFlyAndSwim这个接口
classPerson3implementsIMyFlyAndSwim{
fly(){
console.log('我飞了2')
}
swim() {
console.log('我游泳2')
}
}
})()
如果你使用过 C# 或 Java,你会对这种语法非常熟悉。 我们声明一个 Greeter 类。这个类有 3 个成员:一个叫做 message 的属性,一个构造函数和一个 greet 方法。
你会注意到,我们在引用任何一个类成员的时候都用了 this。 它表示我们访问的是类的成员。
后面一行,我们使用 new 构造了 Greeter 类的一个实例。它会调用之前定义的构造函数,创建一个 Greeter 类型的新对象,并执行构造函数初始化它。
最后一行通过 greeter 对象调用其 greet 方法
类
//类:可以理解为模板,通过模板可以实例化对象
//面向对象的编程思想
(() => {
// ts 中类的定义及使用
class Person {
// 定义属性
name:string
age:number
gender:string
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(name:string='大傻春',age:number=18,gender:string='男'){
//更新对象中的属性数据
this.name = name
this.age = age
this.gender = gender
}
// 定义实例方法
sayHi(str:string){
console.log(`大家好,我是${this.name},今年${this.age},性别${this.gender}`,str)
}
}
//ts中使用类,实例化对象,可以直接进行初始化操作
const person = new Person('二傻',10,'女')
person.sayHi('你是谁')
})()
继承
// 继承:类与类之间的关系
// 继承后类与类之间的叫法:
// A类继承了B这个类,那么A类叫做子类,B类叫做基类
// 子类:派生类
// 基类:超类(父类)
// 发生了继承关系就出现父子关系
(() => {
// 定义一个类,作为基类(超类/父类)
class Person {
name:string
age:number
gender:string
//定义构造函数
constructor(name:string = '小明',age:number=18,gender:string = '男') {
//更新属性数据
this.name = name
this.age = age
this.gender = gender
}
//定义实例方法
sayHi(str:string) {
console.log(`我是:${this.name},今年${this.age},${this.gender},${str}`)
}
}
// 定义一个类,继承自Person
class Student extends Person {
constructor(name:string,age:number,gender:string){
// 调用的父类的构造函数,使用的是super
super(name,age,gender)//super()访问和调用一个对象父对象上的函数
}
//可以调用父类中的方法
sayHi(){
console.log('学生类的sayHi方法')
super.sayHi('1')
}
}
// 实例化Person
const person = new Person()
person.sayHi('2')
//实例化Student
const stu = new Student('3',16,'女')
stu.sayHi()
// Student先使用extends关键字来继承Person类,然后实例化Student类stu,然后stu方法使用父级方法sayHi
//子类调用父类的构造函数使用的是super关键字
//子类可以重写父类的方法
})()
多态
// 多态: 父类型的引用指向了子类型的对象,不同类型的对象针对相同的方法,产生了不同的行为
(()=>{
// 定义父类
classAnimal{
name:string
//定义一个构造函数
constructor(name:string){
// 更新数据值
this.name=name
}
// 实例方法
run(distance:number=1){//distance距离
console.log(this.name,`跑了${distance}米`)
}
}
//定义子类
classDogextendsAnimal{
//构造函数
constructor(name:string){
//调用父类的构造函数,实现子类中属性的初始化
super(name)
}
//实例方法
run(distance:number=5){//distance距离
console.log(this.name,`跑了${distance}米`)
}
}
//定义子类
classPigextendsAnimal{
//构造函数
constructor(name:string){
//调用父类的构造函数,实现子类中属性的初始化
super(name)
}
//实例方法
run(distance:number=10){//distance距离
console.log(this.name,`跑了${distance}米`)
}
}
// 实例化父对象
constani:Animal=newAnimal('动物')
ani.run()
//实例化子对象
constdog:Dog=newDog('大黄')
dog.run()
// 实例化子类对象
constpig:Pig=newPig('猪猪侠')
pig.run()
// 父类和子类的关系:父子关系,此时,父类类型创建子类的对象
constdog1:Animal=newDog('小黄')
dog1.run()
constpig1:Animal=newPig('小猪猪侠')
pig1.run()
// 该函数需要的参数是Animal类型的
functionshowRun(ani:Animal) {
ani.run()
showRun(dog1)
showRun(pig1)
}
})()
// 修饰符(类中成员的修饰符):主要是描述类中的成员(属性,构造函数,方法)的可访问性
// 类中的成员都有自己的默认访问符,public
// public修饰符---公共的,类中的成员默认的修饰符,代表的是公共的。任何位置都可以访问类中的成员
// private修饰符--- 私有的,类中的成员如果使用private来修饰,那么外部是无法访问这个成员数据的,当然,子类也无法访问该成员数据
// protected修饰符,类中的成员使用protected来修饰,那么外部无法访问这个成员的数据,子类却可以访问。
(()=>{
//定义一个类
classPerson{
// public name:string
// private name:string
protectedname:string
}
})()
读取器 get set
(()=>{
classPerson{
firstName:string
lastName:string
constructor(firstName:string,lastName:string){
this.firstName=firstName
this.lastName=lastName
}
//读取器
getfullName() {
console.log
returnthis.firstName+'_'+this.fullName
}
//修改器
setfullName(aaa) {
letnames=aaa.split('_')
this.firstName=names[0]
this.lastName=names[1]
}
}
constperson:Person=newPerson('西门','吹雪')
console.log(person)
person.fullName='南宫_问天'
console.log(person.fullName)
})()
静态成员(静态属性/静态方法)
// 静态成员: 在类中通过static修饰的属性或者方法,那么就是静态的属性及静态的方法,也称为静态成员
// 静态成员在使用的时候是通过类名.的语法来调用的
(()=>{
classPerson{
// 静态属性
// 类中默认有一个内置的name属性
staticname1:string='123'
// 构造函数是不能通过static来进行修饰的
//static constructor(){} error
// 静态属性
staticsayHi(){
console.log('321')
}
}
//const person:Person =new Person('456')
// 通过类名.静态方法来调用内部的静态方法
Person.sayHi()
// 通过类名.静态属性来访问成员数据
console.log(Person.name1)
// 通过类名. 静态方法的方式来设置成员数据
Person.name1='999'
console.log(Person.name1)
})()
抽象类
// 抽象类:包含抽象方法(抽象方法一般没有任何的具体内容的实现),
//也可以包含实例方法,抽象类是不能被实例化的,为了让子类进行实例化及实现内部的抽象方法
(()=>{
// 定义一个抽象类
abstractclassAnimal{
//抽象方法
abstracteat()// 抽象方法不能有具体的实现
//实例方法
sayHi(){
console.log('吃')
}
}
// 抽象类的对象是无法实例化的
//const ani:Animal = new Animal /error
//定义一个子类()派生类
classDogextendsAnimal{
eat(){
console.log('飞着吃')
}
}
})()
// 总结感受,有点像接口,先把子类的各项类似的方法抽象出来,在父类中抽象方法
函数
// 函数:一些被频繁使用的代码封装起来。在需要的时候直接调用即可
(()=>{
// // js写法
// // 函数声明,命名函数
// function add(x,y){
// return x + y
// }
// //函数表达式,匿名函数
// const add2 = function (x,y){
// return x + y
// }
//-----------------------------------
// ts写法
// 函数声明,命名函数
functionadd(x:string,y:string):string{
//括弧内参数的:string指的是传入的x是string类型括弧外的:string是指函数的返回值是string
returnx+y
}
//函数表达式,匿名函数
constadd2=function(x:number,y:number):number{
returnx+y
}
//ts函数的完整写法
//函数本身类型中的 => 和 es6 没关系,是 ts 中关于定义函数本身类型的语法
//add3--->变量名--->函数add3
//(x:number,y:number) => number 当前这个函数的类型 =>在这里不是箭头函数,是定义函数的类型
//function (x:number,y:number):number {return x+y} 就相当于符合上面函数的值
constadd3:(x:number,y:number)=>number=function(x:number,y:number) :number{
returnx+y
}
constas=add3(10,20)
console.log(as)
})()
可选?和默认参数
// 可选参数:函数在声明的时候,内部的参数使用了?进行了修饰,那么就表示该参数可以传入也可以不传入
// 默认参数:函数在声明的时候内部的参数有自己的默认值,此时的这个参数就可以叫做默认参数。
(()=>{
constgetFullName=function(firstName:string='东方',lastName?:string):string{
if(lastName){
returnfirstName+'_'+lastName
}else{
returnfirstName
}
}
// 函数调用
// 什么也不传入
console.log(getFullName())
// 只传入姓氏
console.log(getFullName('诸葛'))
//传入姓氏和名字
console.log(getFullName('诸葛','亮'))
})()
剩余参数
// 剩余参数(rest参数)
// 剩余参数是放在函数声明的时候所有的参数的最后
(()=>{
// ...args:string[]---->剩余的参数,放在了一个字符串的数组中,args里面
functionshowMsg(str:string,...args:string[]){
console.log(str)//a
console.log(args)//b c d e
}
showMsg('a','b','c','b')
})()
函数重载
// 函数重载:函数的名字不同,函数的参数及个数不同
(()=>{
// 定义一个函数
//函数重载声明
functionadd(x:string,y:string):string
functionadd(x:number,y:number):number
// 函数声明
functionadd(x:string|number,y:string|number):string|number{
if(typeofx==='string'&&typeofy==='string') {
returnx+y
}elseif(typeofx==='number'&&typeofy==='number') {
returnx+y
}
}
// 函数调用
console.log(add('诸葛','孔明'))
// 两个参数都是数字
console.log(add(10,20))
//函数重载应用, 传入的是非法的数据,ts给出提出错误信息的内容,报红色。
console.log(add('诸葛',12))
console.log(add(23,'孔明'))
})()
泛型
泛型
// 泛型:在定义函数、接口、类的时候、不能预先确定要使用的数据的类型,而是在使用函数、接口、类的时候才能确定数据的类型
// 使用方法:<>
(()=>{
function getArr4<T>(value:T,count:number):T[]{
// <T> T 类似形参,根据需要定
const arr :Array<T> = []
for (let i= 0;i< count;i++){
arr.push(value)
}
return arr
}
const arr1 = getArr4<number>(200.1234,5)
//在使用的使用,<>里面的规定数据类型
const arr2 = getArr4<string>('abcdefg',5)
console.log(arr1)
console.log(arr1[0].toFixed(3))
console.log(arr2[0].split(''))
})()
多个泛型参数
(()=>{
// 多个泛型参数的函数:函数中有多个泛型的参数
function getMsg<K,V>(value1:K,value2:V):[K,V] {
return [value1,value2]
}
const arr1 = getMsg<string,number>('jack',100.2345)
console.log(arr1[0].split(''))
console.log(arr1[1].toFixed(1))
})()
泛型接口
//泛型接口:在定义接口时,为接口中的属性或者方法定义泛型类型,在使用接口的时候再具体指定具体的泛型接口
(()=>{
// 定义一个泛型的接口
interfaceIBaseCRUD<T>{
data:Array<T>
add: (t:T)=>T
getUserId: (id:number)=>T
}
// 定义了用户信息的类
classUser{
id?:number
name:string
age:number
constructor(name:string,age:number) {
this.name=name
this.age=age
}
}
//再定义一个增删改查操作的类
//CRUD----create,read,update,delete
classUserCRUDimplementsIBaseCRUD<User>{
data:Array<User>=[]
add(user:User):User{
user.id=Date.now()+Math.random()
this.data.push(user)
returnuser
}//储存用户信息
getUserId(id:number):User{
returnthis.data.find(user=>user.id===id)
}
//根据id查找用户信息对象
}
//实例化添加用户信息对象的类UserCRUD
constuserCRUD=newUserCRUD()
userCRUD.add(newUser('jack',20))
userCRUD.add(newUser('bob',18))
const{id}=userCRUD.add(newUser('tom',22))
constuser=userCRUD.getUserId(id)
console.log(userCRUD.data)
console.log(user)
})()
泛型类
(()=>{
// 定义一个类,类中的属性值的类型是不确定的,方法中的参数和返回值的类型也是不确定
// 定义一个泛型类
classGenericNumber<T>{
//默认的属性的值是泛型类型
defaultValue:T
//函数本身类型中的 => 和 es6 没关系,是 ts 中关于定义函数本身类型的语法
add:(x:T,y:T)=>T
}
//在实例化类的对象时候,再确定泛型的类型
constg1:GenericNumber<number>=newGenericNumber<number>()
// 设置属性值
g1.defaultValue=100
// 相加的方法
g1.add=function(x,y) {
returnx+y
}
})()
泛型约束
(()=>{
//如果直接对一个泛型参数取length属性,会error,因为泛型不知道他有这个属性
// 定义一个接口,用于约束将来的莫个类型中必需的length属性
interface ILength{
// 接口中有一个属性:length
length:number
}
function getLength<T extends ILength>(x:T):number{
return x.length
}
console.log(getLength<string>('afsdfdsafsad'))
console.log(getLength<number>(1234))// 因为number没有length,所有不满足约束,error
})()
其他
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能
很多的第三方库都定义了对应的声明文件库, 库文件名一般为 @types/xxx, 可以在
https://www.npmjs.com/package/package 进行搜索
有的第三库在下载时就会自动下载对应的声明文件库(比如: webpack),有的可能需要单独下载(比如jQuery/react)
例如:下载jQuery声明文件: npm install @types/jquery --save-dev
什么是声明语句
假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 ` 标签引入jQuery,然后就可以使用全局变量$或jQuery` 了。
但是在 ts 中,编译器并不知道 $ 或 jQuery 是什么东西
/*
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
声明语句: 如果需要ts对新的语法进行检查, 需要要加载了对应的类型说明代码
declare var jQuery: (selector: string) => any;
声明文件: 把声明语句放到一个单独的文件(jQuery.d.ts)中, ts会自动解析到项目中所有声明文件
下载声明文件: npm install @types/jquery --save-dev
*/
jQuery('#foo');
// ERROR: Cannot find name 'jQuery'.
这时,我们需要使用 declare var 来定义它的类型
declarevarjQuery: (selector:string)=>any;
jQuery('#foo');
declare var 并没有真的定义一个变量,只是定义了全局变量 jQuery 的类型,仅仅会用于编译时的检查,在编译结果中会被删除。它编译结果是:
jQuery('#foo');
一般声明文件都会单独写成一个 xxx.d.ts 文件
创建 01_jQuery.d.ts, 将声明语句定义其中, TS编译器会扫描并加载项目中所有的TS声明文件
declarevarjQuery: (selector:string)=>any;
内置对象
JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。
内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
ECMAScript 的内置对象
BooleanNumberStringDateRegExpError
/* 1. ECMAScript 的内置对象 */
letb:Boolean=newBoolean(1)
letn:Number=newNumber(true)
lets:String=newString('abc')
letd:Date=newDate()
letr:RegExp=/^1/
lete:Error=newError('error message')
b=true
// let bb: boolean = new Boolean(2) // error
BOM 和 DOM 的内置对象
WindowDocumentHTMLElementDocumentFragmentEventNodeList
constdiv:HTMLElement=document.getElementById('test')
constdivs:NodeList=document.querySelectorAll('div')
document.addEventListener('click', (event:MouseEvent)=>{
console.dir(event.target)
})
constfragment:DocumentFragment=document.createDocumentFragment()
网友评论