一、ts介绍
是javascript的超集,遵循最新的ES5 ES6规范。ts扩展了js的语法。
二、安装与编译
Npm install -g typescript cnpm install -g typescript yarn global add typescript
查看安装成功与否 新打开终端 输入:tsc -v
运行: tsc helloword.ts
vscode编译:因为浏览器不认识ts代码需要我们编译, 在终端输入tsc + 文件名.后缀
会在同级生成一个以js结尾的文件
为了节省开发时间,每次写好ts代码后能够自动编译
1.创建
ts.config.json
的文件tsc --init
生成配置文件 修改配置文件中outDir
可配置编译后的Js存放位置
2.vscode点击:任务->运行任务-> tsc:监视-tsconfig.json(老版本vscode)
终端-> 运行任务-> typescript -> 监视tsconfig.json 即可
三、数据类型
ts中为了使编码更规范,更有利于维护,增加了类型校验:
布尔类型(bollean)
数字类型(number)
字符串类型(string)
数组类型(array)
元组类型(tuple)
枚举类型(enum)
任意类型(any)
null和undefined
void类型
never类型
1.布尔类型
eg. var flag:boolean=true
如果写falg = “str”
,编译不通过会报错
2.数字类型(整数、浮点数都ok)
Var a:number = 123
3.字符串类型
4.数组类型 OK 有两种方式
方式一:let arr:nuber[] = [1, 2, 3, 4]
方式二:let arr:Array<number>=[11, 23, 353, 34]
5.元组类型(属于数组的一种,可以指定数组中每一项的多种类型)
Let arr:[string, number, boolean] = [“ts”, 1, true]
6.**枚举类型 ** (用于标识状态和固定值)
随着计算机的不断普及,程序不仅只用于数值计算,还更广泛用于处理非数值的 数据。例如:性别、月份、颜色、单位、学历等,都不是数值数据。
事先考虑到某一变量可能去的值,尽量用自然语言中含义清楚的单词来表示他的每一个值,这种方法称为枚举方法
enum 枚举名{
标识符[=整型常数],
标识符[=整型常数],
...
标识符[=整型常数]
}
Enum flag {
Success=1,
Error=-1
}
Let f:flag = flag.success / flag.error就代表一个值
若枚举中没有赋值的话,会输出占位的索引
如果是:
Enum color {red, blue=5, orange}
Let c:color=color.red 输出c为其索引 0
Let c:color = color.blue 输出c为其值 5
Let c:color = color.orange 输出c为 6 自动跟随前一个进行累加
7.任意类型 any
Let num:any=123
Num=”str”
用于获取节点之后操作dom可以使用any类型
8.**null
undefined
**(其他never类型的子类型)
如果定义了变量未赋值,输出个该变量为undefined 但是会报错
所以要声明变量的时候,在变量后加undefined
如果是该变量可能是number 也可能是undefined 就 let num:number | undefined
null也是如此
一个元素是多种类型,any类型可以囊括这些内容,还有一个其他形式:
Let num:number | null | undefiend
9.void类型 :表示没有任何类型,一般用于定义方法的时候方法没有返回值
Function run():void {
Console.log(‘run’)
}
run();
10.never
类型 是其他类型,(null undefined的子类型)代表不会出现的值。意味着声明never的变量只能被never类型所赋值
Let a:undefined;
a = undefined
Let b:null;
b = null;
Let a :never;
a=132; //错误写法
a =(() => {
Throw new Error(‘错误”)
})() //正确写法
四、函数
函数也是需要指明类型的
//函数声明类
function run():string {
return 'run'
}
fun2();
//匿名函数
var fun2=function():number {
return 123
}
fun2();
ts中定义方法传参
//函数声明
function getInfo(name: string, age:number):string {
return `${name} --- ${number}`
}
console.log(getInfo('zhangsan', 20));
//匿名函数
let getInfo2 = function(name: string, age:number):string {
return `${name} --- ${number}`
}
console.log(getInfo('zhangsan', 20));
没有返回值得函数
function run():void {
console.log('run')
}
run();
方法的可选参数
es5方法的实参和形参可以不一样,但是ts不可以,此时需要配置可选参数
即在可能不会传递的参数后面添加 ?
function getInfo(name: string, age?:number):string {
if (age) {
return `${name} --- ${number}`
} else {
return `${name} --- 就不告诉你 `
}
}
//alert(getInfo('张三', 123)) 都可输出
//alert(getInfo('张三')) 都可输出
注意:可选参数必须配置到参数的最后面
默认参数
es5里面没法设置默认参数,es6和ts都可以设置默认参数
function getInfo(name: string, age:number=12):string {
if (age) {
return `${name} --- ${number}`
} else {
return `${name} --- 就不告诉你 `
}
}
//alert(getInfo('张三')) 都可输出,且age是默认的
//alert(getInfo('张三', 123)) 都可输出
剩余参数
function sum(a: number, b:number, c:number, d:number):number {
return a + b+c+d
}
sum(1, 2, 3, 4)
//三点运算符(剩余参数) 接受形参传来的值
function sum(...result:number[]):number {
let sum = 0;
for(var i = 0; i < result.length; i ++) {
sum += result[i];
}
return sum
}
sum(1, 2, 3, 4, 5, 6)
//剩余参数三点运算另一种形式
function sum(a:number, ...result:number[]):number {
let sum = a;
for(var i = 0; i < result.length; i ++) {
sum += result[i];
}
return sum
}
sum(1, 2, 3, 4, 5, 6)
函数重载
java中方法的重载,重载的是两个或两个以上同名函数,但他们的参数不一样,这时会出现函数重载的情况
ts中的重载,通过为同一个函数提供多个函数类型定义来试下多种功能的目的
ts为了见佛那个es5和es6 重载的写法 和java有区别
//es5中 出现同名方法,后面的会替换上面的方法
function css(config:any):any {
...
}
function css(config:any, value:any):any {
...
}
//ts中的重载
function getInfo(name: string):string;
function getInfo(age: number):number;
function getInfo(str: any):any {
if(typeof str === 'string') {
return '我是,’+ str;
} else {
return ‘我的年龄是,’ + str
}
}
alert(getInfo('张三')) //正确
alert(getInfo(12)) //正确
alert(getInfo(true)) //错误
//----------------------------------------------
//参数一样
function getInfo(name: string):string;
function getInfo(name: string, age: number):string;
function getInfo(name: string, age?:any):any {
if (age) {
return '我叫,' + name + '我的年龄是' + age;
} else {
return '我叫, ' + name
}
}
alert (getInfo('张三')) //正确
alert (getInfo(123)) //错误
alert (getInfo('张三', true)) //错误
箭头函数
this指向的问题, 箭头函数的this指向上下文
//es5
setTimeout(function() {
alert('run')
}, 1000)
//箭头函数
setTimeout(() => {
alert('run')
}, 1000)
五、ts中的类
- 通过
class
类来定义
class Person {
name:string; //属性 前面省略了public关键词
constructor(n:string) { //构造函数 实例化类的时候触发的方法
this.name = n;
}
run():void {
alert(this.name);
}
}
let p = new Person('张三');
p.run();
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
getName():string {
return this.name;
}
setName():void {
this.name = name;
}
}
let p = new Person('张三')
alert(p.getName());
p.setName('李四')
alert(p.getName());
- ts中实现继承 extends / super
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
run():void {
return `${this.name}在运动`
}
}
//继承
class Web extends Person {
constructir(name: string) {
super(name); /*初始化父类的构造函数*/
}
let w = new Web('李四');
alert(w.run());
ts中继承 父类的方法和子类的方法一致
如果父类和子类都有同样的方法 调用子类的就会执行子类方法,先去找子类再找父类类里的修饰符
提供了三种修饰符
-
public
: 表示共有。 在当前类里面、子类、类外部都可以访问 -
protected
: 表示保护类型。 在当前类里面、子类里面可以访问, 类外部无法访问 //控制台会报错,但是会输出, -
private
: 表示私有。 在当前类里面可以访问,子类和类外部都无法访问
属性如果不加修饰符 默认为共有public
class Person {
public name: string; //公有属性
constructor(name: string) {
this.name = name;
}
run():string {
return `${this.name}在运动`
}
}
class Web extends Person {
constructir(name: string) {
super(name); /*初始化父类的构造函数*/
}
let w = new Web('李四');
alert(w.run());
- 静态属性 静态方法
class Person {
public name: string
public age: number=12
//静态属性才能在静态方法中调用
static sex = "男"
constructor(name:string) {
this.name = name
}
run() {
alert(`${this.name}在运动`) //实例方法
}
work() {
alert(`${this.name}在工作`) //实例方法
}
//静态方法 没法直接调用类里的属性
static print() {
alert('方法' + this.age) //会报错
}
static print() {
alert('方法' + Person.sex) //调用成功
}
}
let p = new Pweson('张三');
p.run();
p.work();
//调用静态方法
Person.print();
//使用静态属性
alert(Person.sex)
-
多态:父类定义一个方法不实现,让继承他的子类去实现 每一个子类有不同的表现
//多态也是一种继承的表现,属于继承
class Animal {
name:string;
constructor(name: string) {
this.name = name
}
eat() {
console.log('吃的方法')
}
}
class Dog extends Animal {
construactor(name: string) {
super(name)
}
eat() {
return this.name + '吃狗粮'
}
}
class Cat extends Anmal {
construactor(name: string) {
super(name)
}
eat() {
return this.name + '吃猫粮'
}
}
-
抽象类:它是提供其他类继承的基类,不能直接被实例化
用abstract
关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含其具体实现并且必须在派生类中实现 abstract抽象方法只能放在抽象类里面 抽象类和抽象方法用来定义标准
//抽象类和抽象方法用来定义标准 标准:Animal这个类要求他的子类必须包含eat方法
abstract class Animal {
abstract eat():any;
}
let a = new Animal() //错误的写法 不能直接被实例化
正确写法
//抽象类和抽象方法用来定义标准 标准:Animal这个类要求他的子类必须包含eat方法
abstract class Animal {
public name:string;
constructor(name:string) {
this.name = name;
}
abstract eat():any; //该方法必须实现
run() {
console.log('其他方法可以不实现')
}
}
let a = new Animal() //错误的写法
class Dog extends Animal {
constructor(name:any) {
super (name);
}
//抽象类的子类必须实现抽象类里面的抽象方法
eat() {
console.log(this.name + '吃粮食')
}
}
let d = new Dog('小花花');
d.eat();
六、ts中的接口
接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范作用,接口起到一种限制和规范的作用,接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据,也不关心这些类里方法的实现细节,它之规定这批类里必须提供某些方法,提供这些方法的类就可以满足实际需要。ts中的接口类似于java,同时还增加了更灵活的接口类型,包括属性、函数、可索引和类等。 相当于定义标准。
通过
interface
进行定义
- 属性类接口
- 对传入对象参数进行约束
//就是传入对象的约束 是属性接口
interface FullName {
firstNmae: string; //注意 以分号结束
secondName: string;
}
function printName(name:FullName) {
//必须传入对象 firstName secondName
console.log(name.firstName + '--' + name.secondName)
}
printName('123') //错误的写法
printName({firstName: '张', secondName: '三'}) //正确
printName({ age: 20, firstName: '张', secondName: '三'}) //错误写法 因为多了age,内容必须只有firstName和secondName
let obj = {
age: 20, //但是当调用的时候就会报错
firstName: '张',
secondName: '三'
}
printName(obj); //正确写法 且推荐此使用 外层定义只要有firstName和secondName就行
- 对批量方法进行约束
interface FullName {
firstNmae: string; //注意 以分号结束
secondName: string;
}
function printName(name:FullName) {
//必须传入对象 firstName secondName
console.log(name.firstName + '--' + name.secondName)
}
function printInfo(name:FullName) {
//必须传入对象 firstName secondName
console.log(name.firstName + name.secondName )
}
let obj = {
age: 20,
firstName: '张',
secondName: '三'
}
printName(obj);
printInfo({firstName: '李', secondName: '四'})
- 可选属性
interface FullName {
firstName: string;
secondName?: string; //可传可不传
}
function getName(name: FullName) {
console.log(name)
}
getName({firstName: 'xixixi', secondName: '啦啦'})
getName({firstName: 'xixixi'}) //也可编译成功
- 函数类型接口: 对方法传入的参数 以及返回值进行约束 可以做批量约束
//实现加密的函数类型接口
interface encrypt {
(key: string, value: string): string; //参数是key和value 返回值是string类型
}
let md5:encrypt = function(key: string, value: string):string {
//模拟操作
return key + value
}
md5('name', 'zhangsan')
- 可索引接口,数组、对象的约束(不常用)
//定义数组的方式
let arr:number[] = [123, 34555]
let arr1:Array<string>=['23g', 'err4']
//可索引接口 对数组的约束 1
interface UserArr {
[index: number]:string
}
let arr:UserArr=['aaaa', 'bbbb'];
console.log(arr[0])
//对对象的约束
interface UserObj{
[index:string]:string
}
let arr:UserObj = {name: '20'}
-
类类型接口:对类的约束 和 抽象类相似
implements
不是继承,是实现接口
interface Animal {
name: string;
eat(str: string):void;
}
class Dog implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat() {
console.log(this.name + '吃狗粮')
}
}
let d = new Dog('小黑')
d.eat();
class Cat implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat(food:string) {
console.log(this.name + food)
}
}
let c = new Cat('小花')
d.eat('老鼠')
- 接口扩展,接口可以继承接口
interface Animal {
eat(): void;
}
interface Person extends Animal { //person继承animal
work();
}
class Web implements Person {
public name:string;
constructor(name: string) {
this.name = name
}
eat() {
console.log(this.name + '喜欢吃馒头')
}
work() {
console.log(this.name + '喜欢工作')
}
}
let w = new Web('小李')
w.eat()
interface Animal {
eat(): void;
}
interface Person extends Animal { //person继承animal
work();
}
class Programmer {
public name:string;
constructor(name: string) {
this.name = name
}
coding(code:string) {
console.log(this.name + code)
}
}
class Web extends Programmer implements Person { //既继承又实现接口
constructor(name: string) {
super(name)
}
eat() {
console.log(this.name + '喜欢吃馒头')
}
work() {
console.log(this.name + '喜欢工作')
}
}
let w = new Web('小李')
w.eat()
w.coding('写ts代码')
七、ts中的泛型
-
泛型的定义
泛型:软件工程中,我们不仅要创建一致的定义好的API,同时也要考虑重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。
在像C#和java这样的语言中,可以使用泛型来创建可重用的组件,一个组件可以支持多种类型的数据。这样用户就可以以自己的数据类型来使用组件。
通俗理解:泛型就是解决 类、接口、方法的复用性、以及对不特定数据类型的支持
- 泛型函数
//例如同时返回两个不同类型的函数 容易代码冗余
function getData1(value: string):string {
return value;
}
function getData2(value: number): number {
return value
}
//可以使用any类型来进行同时输出,但是使用any放弃了类型检查,传入什么,返回什么
//泛型就出现了
泛型可以支持不特定的数据类型 要求,传入的参数和返回的参数一致。
T
或者是任意大写字母。(建议使用T
)表示泛型,具体什么类型是调用这个方法的时候决定的
function getData<T>(value: T):T {
return value;
}
getData<number>(123);
function getData<T>(value: T): any {
return 'sfdsfgfdgdfg'
}
getData<number>(123); //参数必须是number
getData<string>('dfdsfdsfg');
- 泛型类
//泛型类: 比如有个最小堆算法,需要同时支持返回数字和字符串两种类型 通过类的泛型来实现
class MinClass {
piblic list:number[] = [];
add(num: number) {
this.list.push(num)
}
min() {
let minNum = this.list[0];
}
}
- 泛型接口
//函数类型接口
interface ConfigFn{
(value1: string, value2: string): string;
}
let setData:ConfigFn = function(value1: string, value2: string):string {
retirn value1+value2;
}
setData('name': '张三')
//第一种泛型接口
interface ConfigFn{
<T>(value: T): T;
}
let getData: ConfigFn = function<T>(value: T):T {
return value;
}
getData<string>('张三') //正确
getData<string>(1234) //错误
//第二种泛型接口
interface ConfigFn<T>{
(value: T): T;
}
function getData<T>(value: T):T {
return value;
}
let myGetData:ConfigFn<string>=getData;
myGetData('20'); //正确
myGetData(20); //错误
-
泛型类--延伸
1.定义个User类 作用:隐射数据库字段
2.定义个MysqlDb的类 用于操作数据库
3.然后把User类作为参数传入到MysqlDb中
//把类作为参数来约束数据传入的类型
class User {
username: string | undefined;
password: string | undefined;
}
class MysqlDb {
add(user: User): boolean {
// console.log(user);
return true;
}
}
let u = new User();
u.username = '张三';
u.password = '123456';
let Db = new MysqlDb();
Db.add(u);
//把类作为参数来约束数据传入的类型
class ArticleCate {
title: string | undefined;
desc: string | undefined;
status: number | undefined;
}
class MysqlDb {
add(info: ArticleCate): boolean {
// console.log(info);
return true;
}
updated(info: T, id: number): boolean {
console.log(info)
console.log(id)
return true
}
}
let a= new ArticleCate();
a.title = "国内";
a.desc = "国内新闻";
a.status = 1
let Db = new MysqlDb();
Db.add(a);
发现以上两种写法在每操作一次数据库表的时候 都需要重新指定值得类型,所以使用泛型动态的定义类型,避免代码重复封装
//操作数据库的泛型类
class MysqlDb<T> {
add(info: T): boolean {
// console.log(info);
return true;
}
}
//给User表增加数据
//1.定义一个user类 和数据库进行映射
class User {
username: string | undefined;
password: string | undefined;
}
let u = new User();
u.username = '张三';
u.password = '123456';
let Db = new MysqlDb<User>(); //可以对传入的数据进行校验
Db.add(u);
//赋值方式2 使用构造函数将值提前赋进去
class User {
username: string | undefined;
password: string | undefined;
constructor(params: {
username: string | undefined,
password: string | undefined,
//或者是可选参数 记得加?
}) {
this.username = params.username;
this.password = params.password;
}
}
let u = new User({
username: '张三',
password: '123456'
});
let Db = new MysqlDb<User>(); //可以将类作为参数传入到泛型类中进行数据的约束及验证
Db.add(u);
-
ts类型、接口、类、泛型综合使用--ts封装统一操作Mysql Mongodb Mssql的底层库
定义一个操作数据库的库 支持Mysql Mongodb Mssql
要求1 Mysql Mongodb Mssql功能一样 都有add update delete get方法
注意:约束统一的规范,以及代码重用
解决方案:需要约束规范所以要定义接口,需要代码重用所以用到泛型
1.接口:在面向对象的编程中,接口是一种规范性的定义,它定义了行为和动作的规范
2.泛型 通俗理解:泛型就是解决 类 接口 方法的复用性
interface DBI<T> {
add(info: T): boolean;
update(info: T, id: number): boolean;
delete(id: number): boolean;
get(id: number): any[];
}
//定义一个操作mysqul数据库的类 注意:要实现泛型接口 这个类也应该是一个泛型类
class MysqlDb<T> implements DBI<T> {
constructor() {
console.log('数据库建立连接')
}
add(info: T): boolean {
console.log(info)
return true
}
update(info: T, id: number): boolean {
throw new Error('Method not implemented')
}
delete(id: number): boolean {
throw new Error('Method not implemented')
}
get(id: number): any[] {
let list = [
{
title: ‘xxx’,
desc: 'xxxxx'
},
{
title: ‘xxx’,
desc: 'xxxxx'
},
]
return list;
}
}
//定义一个操作mssqul数据库类型的类
class MsSqlDb<T> implements DBI<T> {
constructor() {
console.log('数据库建立连接')
}
add(info: T): boolean {
throw new Error('Method not implemented')
}
update(info: T, id: number): boolean {
throw new Error('Method not implemented')
}
delete(id: number): boolean {
throw new Error('Method not implemented')
}
get(id: number): any[] {
throw new Error('Method not implemented')
}
}
//操作用户表 定义一个类和数据表做映射
class User {
username: string | undefined;
password: string | undefined;
}
let u = new User();
u.username = '战三';
u.password = "123456";
let oMysql = new MysqlDb<User>();
oMysql.add(u)
八、ts中的模块
模块的概念(官方)
关于属于的一点说明:ts1.5里的术语名已经发生了变化。 “内部模块”现在称作“命名空间”。“外部模块”现在简称为“模块” 模块在其自身的作用域里执行,而不是在全局作用域里。
这意味着定义在一个模块里的变量,函数,类等等在模块外部是不可见的,除非你明确的使用export形式一导出他们。相反,如果想使用其他模块导出的变量,函数、类、接口等的时候,你必须要导入他们,可以使用import形式之一
模块的概念(自己的理解)
我们可以把一些公共的功能单独抽离成一个文件作为一个模块。
模块里面的变量 函数 类等默认是私有的,如果我们要在外部访问模块里面的数据(变量、函数、类)
我们需要通过export暴露莫苦熬里面的数据(变量、函数、类)。
暴露后通过import引入模块就可以使用模块里面暴露的数据(变量、函数、类)
引用名字过长的时候 可以使用 xx as aa来,此后调用使用aa代替xx即可
import { getData as get } from './......'
你是前端嘛?是的话,我就不用写了 和js没差的 不是的话 那你就先成为一个前端再看吧
九、ts的命名空间
在代码量较大的情况下,为了避免各种变量命名冲突,可将相似功能的函数、类、接口等放置到命名空间,可以将代码包裹起来,只对外暴露需要外部访问的对象,命名空间内的对象通过export暴露
命名空间和模块的区别
命名空间:内部模块,主要用于组织代码,避免命名冲突
模块:ts的外部模块简称,例如代码的复用,一个模块里可能有多个命名空间
namespace A {...........} //定义了一堆内容
namespace B {...........} //定义了一堆内容 两者中的内容可以有重复的
如果想要外部暴露,可以在需要暴露的内容前面加export
,使用可以通过命名空间打点使用内部内容即可
把命名空间定义成模块并且暴露使用