TypeScript
官方地址
TypeScript
1. 下载
npm install -g typescript
查看是否安装成功
tsc -v Version 3.9.7
基本使用
helloworld.ts
console.log("ts hello world");
编译ts->js
tsc helloworld.ts 即可生成helloworld.js
配置自动编译
初始化tsconfig.json
tsc --init
找到tsconfig的outdir打开注释改成outDir:./js
然后终端--> 运行任务-->typescript-->tsc 监视
2. 基础类型
在编写ts的时候变量如果声明了是什么类型的,是允许修改的!尽管编译是通过的,但是不建议这么操作
2.1 布尔boolean
let isFlag:boolean = false;
console.log(isFlag)
2.2 数字类型number
let count:number = 0;
count++;
for(let i:number=0;i<10;i++){
console.log(i)
}
2.3 字符串类型
let str:string = '123';
str = 'bob'
console.log(str)
2.4 数组类型
在声明数组的时候就已经限定好数组里面的每一个项的类型了,所以在进行数组的CRUD的时候,都必须要是相同的类型
定义数组的两种方式
let arr:number[] = [123,456]
let arr1:Array = ['aaa','111']
let arr:number[] = [123,456]
let arr1:Array = ['aaa','111']
arr.push(1111)
- 这种是不允许的
arr.push('a')
arr[0] = 45444;
console.log(arr,arr1)
2.5 元组类型
元组就是指定义数组的时候可以指定多种类型,但是指定的类型必须要和数组中的元素一一对应,不允许超出或者缺少
// 允许
let anyArray:[string,number,boolean] = ['1',1,false]
// 不允许
let anyArray:[string,number,boolean] = ['1',1,false,'aaaa']
// 不允许
let anyArray:[string,number,boolean] = ['1',1]
2.6 枚举类型
枚举就是定义一些常量供自己使用 待定
enum Status {
success = 200,
serverError = 500,
noAuth = 403
}
let requestStatus:Status = Status.success;
console.log(requestStatus) // 200
2.7 any类型
定义一个变量为任意类型,你可以重新给他赋任意类型的值
也可以给一个变量指定多种类型
let num:string|boolean|undefined|null;
let a:any ;
a=123;
a=[123]
console.log(a)
let arr4:Array = [1233,'aaa']
let arr5:any[] = ['aaaa',1]
console.log(arr4,arr5)
2.8 void类型
void一般用于函数 标识函数没有返回值
function foo():void{
console.log('foo')
// return 123;
}
// 标识函数的返回值是数字类型
function foo():number{
console.log('foo')
// return 123;
}
foo();
2.9 null和undefined
定义一个变量类型为null和undefined 其用处并不是很大
let u: undefined = undefined;
let n: null = null;
2.10 never
用来告知编译器某处代码永远不会执行,从而获得编译器提示, 以避免程序员在一个永远不会(或不应该)执行的地方编写代码.
function listen() : never{
while(true){
let conn = server.accept()
}
}
listen()
console.log("!!!") //Error: Unreachable code detected.ts(7027)
// 这里声明为never函数表示这个函数调用之后下面的代码不会执行 避免别人在下面继续写代码
function throwError(msg: string ) :never {
throw new Error(msg)
}
throwError("some error")
// 程序报错 代码不会执行到着 所以在这里打印是没有用的
console.log("从这开始代码是不会执行的") //Error: Unreachable code detected.ts(7027)
2.11 object
定义obj类型可能是用来限定函数传入的参数 待验真
let obj:Object ={a:123};
// obj.a = 456; // error
obj = {a:456}
console.log(obj)
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
create("string"); // Error
create(false); // Error
create(undefined); // Error
3. 函数
3.1函数的两种定义方式
ts中的函数注意以下两点
- 函数的返回值类型
- 形参的数据类型
function foo1(a:string,b:number):string{
return 'aaa'
}
const bar2 = function(bar:string):void{
console.log(bar)
}
函数也可以返回一个Promise对象
async getList(payload?: any):Promise
3.2 可选参数和默认参数
可选参数一定要在必须参数的后面
const bar3 = function(bar:string,age?:number=15):void{
if(age){
console.log(age)
}
console.log(bar)
}
3.3 剩余参数
const bar4 = function(...rest:number[]):void{
console.log(rest)
}
const bar5 = function(...rest:any[]):void{
console.log(rest)
}
bar5(12,'aaa') // [12, "aaa"]
3.4 函数重载
同一个函数名,传入不同类型的函数
function hello(name: string): string;
function hello(age: number): number;
function hello(str: any): any {
if (typeof str == 'string') {
return str + 'str'
} else {
return str + 'number'
}
}
hello(123)
snabbdom的列子
export declare function h(sel: string): VNode;
export declare function h(sel: string, data: VNodeData | null): VNode;
export declare function h(sel: string, children: VNodeChildren): VNode;
export declare function h(sel: string, data: VNodeData | null, children: VNodeChildren): VNode;
3.5 箭头函数
和普通函数是一样的
setTimeout(():void => {
console.log('箭头函数')
}, 1000);
4. 类
ts中的类和ES6的类很相似
class Person {
public name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
work() {
console.log(`${this.name}正在学习ts`)
}
}
const per = new Person('liuxuan', 24)
console.log(per)
per.work(); // liuxuan正在学习ts
4.1 继承
class Person {
public name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
work() {
console.log(`${this.name}正在学习ts`)
}
}
class Son extends Person {
public gender: number;
constructor(name: string, age: number, gender: number) {
super(name, age)
this.gender = gender;
}
Console():void {
if (this.gender == 0) {
console.log('男')
} else {
console.log('女')
}
}
}
let son = new Son('刘德华', 15, 0)
son.work(); // 刘德华正在学习ts
son.Console(); // 男
4.2 修饰符
- public 子类 类内部 类外部都可以访问
- private 类内部可以访问
- protected 子类和类内部可以访问
private
class Person {
private name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
work() {
console.log(`${this.name}正在学习ts`)
}
}
let son = new Son('刘德华', 15, 0)
子类和类外部无法访问
let p = new Person('liuxuan',24)
console.log(p.name)// 属性“name”为私有属性,只能在类“Person”中访问。
let son = new Son('刘德华', 15, 0)
console.log(son.name) // 属性“name”为私有属性,只能在类“Person”中访问。
protected
/*
* @Description:
* @Author: 刘宣
* @email: [email protected]
* @Date: 2021-05-11 23:17:18
* @LastEditTime: 2021-05-11 23:41:39
*/
class Person {
protected name: string;
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
work():void{
console.log(`{this.name}正在学习ts`)
}
}
class Son extends Person {
public gender: number;
static address:string = '安徽省';
constructor(name: string, age: number, gender: number) {
super(name, age)
this.gender = gender;
}
Console():void{
if (this.gender == 0) {
console.log('男')
} else {
console.log('女')
}
}
static yourAddr(){
console.log(Son.address)
}
work():void{
console.log(`{this.name}正在学习ts`)
}
}
let p = new Person('liuxuan',24)
p.work()
let son = new Son('刘德华', 15, 0)
son.work() // 刘德华正在学习ts
在类的外部是无法访问的。
console.log(p.name)// 属性“name”受保护,只能在类“Person”及其子类中访问
console.log(s.name)// 属性“name”受保护,只能在类“Person”及其子类中访问
4.3 静态属性和方法
类的静态属性和静态方法都是通过类名来调用的,也就是说有些方法我们不想实例化之后再调用,我们希望直接调用
class Son extends Person {
public gender: number;
static address:string = '安徽省';
constructor(name: string, age: number, gender: number) {
super(name, age)
this.gender = gender;、
}
Console() {
if (this.gender == 0) {
console.log('男')
} else {
console.log('女')
}
}
static yourAddr(){
console.log(Son.address)
}
}
Son.yourAddr(); // 安徽省
4.4 多态
就是指 父亲定义一个方法 子类都有这个方法 只是每个子类的表现(实现)不一样 这就是多态
例如 动物都有吃这个功能 但是狗喜欢吃骨头 猫喜欢吃老鼠 这就是多态
class Animal{
name:string ;
constructor(name:string){
this.name = name
}
eat(){
}
}
class Dog extends Animal{
constructor(name:string){
super(name)
}
eat():void{
console.log(this.name+'吃肉')
}
}
var dog = new Dog('金毛')
dog.eat();
4.5 抽象类
ts中的抽象类:它是提供其他类型继承的基类,不能被实例化
用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不包含具体实现并且必须在派生类中的实现
抽象方法只能出现在抽象类中
抽象类和抽象方法用来定义标准 标准:Animal这个类要求它的子类必须包含eat方法 (也就是说抽象类的抽象方法必须在子类中进行重写)
// 定义一个Animal1抽象类
abstract class Animal1{
abstract foo():any;
}
// Dog1继承Animal1抽象类 并重写Animal1的抽象方法eat
// 不重写父类方法ts就会编译报错
class Dog1 extends Animal1{
constructor(){
super();
}
foo():number{
console.log('抽象方法的抽象类必须要被重写')
return 123;
}
}
console.log(new Dog1().foo())
5.接口 interface
接口:主要就是用来对行为和属性的规范 以及对批量方法的约束
使用:函数的参数(key:interface)
属性和行为的规范
接口的写法与类的写法不同 接口是{}
接口与类的关系:类实现这个接口
不使用接口进行约束实现,是无法实现对批量方法进行约束的
// printLabel 函数规定传入一个对象 对象上必须有一个label属性且必须是字符串类型
function printLabel(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
// 我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);
// 并且同时是无法实现批量方法的约束
// printLabel2 和 printLabel对形参的约束是一样的 但是我们需要重新写一下
function printLabel2(labelledObj: { label: string }) {
console.log(labelledObj.label);
}
换成interface
interface Print{
label:string;
}
printLabel3/printLabel4都是被Print接口约束的
function printLabel3(labelledObj: Print) {
console.log(labelledObj.label);
}
printLabel3(myObj)
function printLabel4(labelledObj: Print) {
console.log(labelledObj.label);
}
printLabel4(myObj)
5.1 可选参数?/只读参数 readonly
// 可选参数/只读属性
// 和函数的可选一样 在名字的后面加上?
interface Print2{
label:string;
age?:number;
readonly gender:number;
}
function printLabel5(labelledObj: Print2) {
console.log(labelledObj)
labelledObj.gender = 45; // 无法分配到 "gender" ,因为它是只读属性
}
printLabel5({label:'111',gender:123,age:24}) // {label: "111", gender: 123}
printLabel5({label:'111',gender:123,age:24}) // {label: "111", gender: 123,age:24}
5.2 接口小案例
/*
* @Description:
* @Author: 刘宣
* @email: [email protected]
* @Date: 2021-05-12 11:27:18
* @LastEditTime: 2021-05-12 13:53:30
*/
interface AjaxConfig {
type: string;
url: string;
data?: any;
dataType: string;
header?: object
}
function Ajax(obj: AjaxConfig) {
var xhr:any = null;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest()
} else {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.open(obj.type,obj.url)
xhr.send(obj.data)
xhr.onreadystatechange = ():void=>{
if(xhr.readyState!=4)return;
console.log(xhr.responseText
)
}
}
Ajax({
type:"GET",
url:"AAA",
dataType:"1111"
})
5.3 对函数进行限制
对函数进行限定 可实现对函数的批量限定,规定被限定的函数必须符合我的接口规范
限定函数的name属性为string类型,age为number类型 返回值为空
interface Func {
(name: string, age: number): void;
}
// 批量对func和func1进行限定
var func: Func = function (name: string, age: number): void {
console.log(name + "==" + age)
}
func(12,22) // Error
func('12',22) // Success
var func1: Func = function (name: string, age: number): void {
console.log(name + "==" + age)
}
func1(12,22) // Error
func1('12',22) // Success
5.4 对类类型进行限定
对类的类型进行限定。类和类之间叫实现 implements
规定:凡是实现我这个类的 必须存在存在name为string 和eat方法 且这两个方法的类型必须要和我接口保持一致
interface Animal {
name: string;
eat(name: string): number;
}
class Dog3 implements Animal {
name: string;
constructor(name: string) {
this.name = name;
}
eat() {
console.log(name + "===吃")
return 123;
}
}
let d = new Dog3('金毛');
d.eat();
5.5 可索引接口
指的是对对象和数组进行限定
索引为number类型就是对数组的限定,索引为字符串类型就是对对象进行限定
数组:
interface Arrays {
// 限定数组的索引为number 且数组项为数字
[index: number]: number
}
let newArr: Arrays = [123, 'aa'] // 不能将类型“string”分配给类型“number”
let newArr1: Arrays = [123]
对象
interface Objects {
// 限定对象的索引为string 对象的key限定为string
[index: string]: string
}
let obj1: Objects = {
'name': 'zs',
age: 18, // 这里的key应该为字符串
}
5.5 接口的扩展(接口继承接口)
接口继承接口 并且实现了类
City类必须要同时符合Area和Province接口规范,
interface Area {
area: string;
}
// 接口继承接口
interface Province extends Area {
province: string;
}
class China {
country: string;
constructor(country: string) {
this.country = country;
}
}
// City类继承China类并实现了Province接口
class City extends China implements Province {
province: string;
area: string;
constructor(province: string, area: string, country: string) {
super(country)
this.province = province
this.area = area
}
hello(){
console.log(this.province,this.area,this.country)
}
}
let C = new City("安徽省", "华东", "china")
C.hello(); // 安徽省 华东 china
6 泛型
泛型:可以支持不特定的数据类型 要求:传入的参数和返回的参数是一致的
具体的类型 T由传入时候的数据类型来决定
泛型就是解决类 接口 方法 的复用性 以及对不特定数据类型的支持
回顾之前 我们在定义函数或者类或者接口的时候 我们都是直接把它们的数据类型写死了 ,这导致他们无法进行一个复用,使用泛型可以有效的解决这类问题
6.1 泛型函数
// 不使用泛型
function foo(name: string): string {
console.log(name)
return name;
}
foo('123')
// 如果我们想传入数字怎么办(可以设置为any类型,但是这样就失去了类型校验的价值)? 再写一份
function foo1(name: number): number {
console.log(name)
return name;
}
foo1(123)
// 到此发现 这显然是很冗余的
// 使用泛型
function foo4(name: T): T {
console.log(name)
return name;
}
foo4('123')
foo4(456)
// 使用泛型之后 foo4函数被重用了
6.2 泛型类
// 普通类
class Foo7 {
name: string;
constructor(name: string) {
this.name = name;
}
printName() {
console.log(this.name)
}
}
let foo7 = new Foo7('liuxuan');
foo7.printName(); // liuxuan
let foo8 = new Foo7(123); // 类型“number”的参数不能赋给类型“string”的参数。
// 到此发现这个类也是存在局限的 因为我们在初始化的时候 只能传入string类型的
泛型类
// 泛型类
class Foo8{
name: T;
constructor(name: T) {
this.name = name;
}
printName(): void {
console.log(this.name, 'printName')
}
}
let foo9 = new Foo8('foo8')
let foo10 = new Foo8(10)
foo9.printName()
foo10.printName()
// 使用泛型类之后,这个Foo8类 显然可以支持更多的类型了 减少了代码的冗余
6.3 泛型接口
// 泛型接口
// 普通接口
interface Bar1 {
name: string;
eat(name: string, food: string): void;
}
class Bar2 implements Bar1 {
name: string;
constructor(name: string) {
this.name = name;
}
eat(): void {
console.log(this.name) // 杨训鹏
}
}
let bar10 = new Bar2('杨训鹏')
bar10.eat();
// 这个接口也是存在局限性的,因为这个接口只支持string类型的
new Bar2(10) // 类型“number”的参数不能赋给类型“string”的参数。
泛型接口
// 使用泛型接口对函数和属性进行泛型限定
interface Bar99 {
name: T;
eat(name: T, food: T): void;
}
class Bar999 implements Bar99 {
name: T;
constructor(name: T) {
this.name = name;
}
eat(name: T, food: T): void {
console.log(name,food,"name-food")
}
}
new Bar999('helloWorld').eat('name','food') // helloWorld
new Bar999(123).eat(1,2) // 123
6.4 把类当作泛型
用来限定传入的类型是我类的实例
案例:封装一个数据库操作类
数据库DBI接口
interface DBI {
add(info: T): boolean;
update(info: T, id: number): boolean;
delete(id: number): boolean;
get(id: number): any[];
}
定义MSsql类
实现泛型接口的类也必须是一个泛型类
class MSSQL implements DBI{
add(info: T): boolean {
console.log(info) // User {username: "wgy", password: 91129}
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.");
}
}
定义一个MYSQL类
class MYSQL implements DBI{
add(info: T): boolean {
console.log(info) // User {username: "wgy", password: 91129}
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.");
}
}
我们要向数据库添加用户 所以要限定传入的数据必须要限定为User类的实例
class User{
username:string;
password:any;
constructor(username:string,password:any){
this.username = username;
this.password = password;
}
}
操作
// 像数据库中插入用户信息
let user = new User('wgy',91129);
// 使用类泛型 来限定传入我数据的数据必须是User的实例
let ms = new MSSQL();
ms.add(user)
// ms.add(123) // 类型“number”的参数不能赋给类型“User”的参数。
7.模块
ts中的模块封装遵循的是ES6的 export/import语法
对上面的DB案例进行封装
新建DB-interface.ts用来保存接口
interface DBI {
add(info: T): boolean;
update(info: T, id: number): boolean;
delete(id: number): boolean;
get(id: number): any[];
}
export default DBI;
新建MYSQL类 操作mysql
import DBI from "./DB-interface"
export default class MYSQL implements DBI{
add(info: T): boolean {
console.log(info) // User {username: "wgy", password: 91129}
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.");
}
}
新建MSSQL类 操作微软的sql
import DBI from "./DB-interface"
export default class MSSQL implements DBI{
add(info: T): boolean {
console.log(info) // User {username: "wgy", password: 91129}
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.");
}
}
新建User类 操作用户信息
export default class User{
username:string;
password:any;
constructor(username:string,password:any){
this.username = username;
this.password = password;
}
}
demo.ts中使用
导入
import MSSQL from "./modules/MSSQL-DB";
import MYSQL from "./modules/MYSQL-DB";
import User from "./modules/User";
使用
// 像数据库中插入用户信息
let user = new User('wgy',91129);
// 使用类泛型 来限定传入我数据的数据必须是User的实例
let ms = new MSSQL();
ms.add(user)
// ms.add(123) // 类型“number”的参数不能赋给类型“User”的参数。
到此就完成了模块话
----demo.ts
----modules
------DB-interface.ts
------MYSQL-DB.TS
------MSSQL-DB.TS
------User.ts
8.命名空间
9.装饰器
9.1 类装饰器
9.2 属性装饰器
10.ts在vue中的使用
前言:在写法上 只有js的部分写法变了 ,主要是以下两个包(装饰器)
vue-property-decorator
vuex-module-decorators
首先先创建一个ts的项目,然后装包(一个是vue的一个是)
cnpm i vue-property-decorator vuex-module-decorators -S
10.1与Vue 写法上的区别
注意:下面的写法都是在export default class About extends Vue {}写的
首先最基本的我们需要引入两个函数吧
import { Component, Vue} from "vue-property-decorator";
@Component声明组件
https://segmentfault.com/a/1190000019906321
@Component
装饰器可以接收一个对象作为参数,可以在对象中声明 components ,filters,directives
等未提供装饰器的选项,也可以声明computed,watch
等 ,目前我还没用到,用到了写
@Component({
name: "About",
components: {
User
}
})
以前的写法
name:"About",
components:{
User
}
10.1.1.vue文件的写法区别
react
class Test extends React.Component{}
ts-vue
export default class About extends Vue {}
js-vue
export default {}
10.1.2.data
// 定义一个User接口 用来约束UserList的数据类型
interface IUser {
name: string;
age: number;
sex?: number;
id: number;
}
private firstName: string = "liu";
private lastName: string = "xuan";
private CPC: any = "v-model自定义组件";
private obj: any = {
names: "zs",
age: 20,
sex: 1,
id: 123
};
userList: IUser[] = [
{
name: "zs",
age: 20,
sex: 1,
id: 123
},
{
name: "wu",
// age: '18', // 受到interface接口的约束
age: 15,
sex: 0,
id: 456
}
];
vue
data(){
return {
firstName:"liu",
lastName:xuan
},
10.1.3 computed
private get fullName():string{
return this.firstName + this.lastName;
}
computed:{
fullName(){
return this.firstName+this.lastName
}
},
10.1.4 watch
import {Watch} from "vue-property-decorator"
@Watch("firstName",{immediate:true,deep:true})
// 这里的名字是随便写的 不固定的
private firstNameChange(newVal: any, oldVal: any): void {
console.log(newVal, oldVal, "我相当于Vue的Watch监听");
}
watch(){
firstName:{
handler:(newval,oldval){
},
deep:true,
immediate:true
}
}
10.1.5 生命周期函数
生命周期函数的写法还是和之前是一样的
public created() {
this.lastName = "wgy";
this.getList();
console.log("我是声明周期函数 我的写法还是和之前是一样的");
}
public mounted() {
console.log("我是声明周期函数 我的写法还是和之前是一样的");
}
10.1.6 普通函数
写法也是和之前一样的 真要说区别的话 多了一些方法的修饰符
private addCount():void {
AboutStore.updateCount(11);
}
private getList():void{
AboutStore.getList().then(res=>{
console.log(res) // {a:123}
})
}
子组件
10.1.7 prop
import { Prop } from "vue-property-decorator";
@Prop(Number) private id!: number; // id后面加上! 表示属性断言 表示这个属性在ts里面一定会有的
@Prop({ type: Number, required:true }) private age!: number;
@Prop({ type: String }) private name!: string;
@Prop({ type: Number }) private sex!: number;
prop:{
age:{
type:Number,
required:true
}
}
10.1.8 emit
@Emit("changeCPCtocpc")
clickHandle(): string {
//这里return得会作为参数传递给父组件
return "cpc-vue1";
}
clickHandleJs(): void {
this.$emit('changeCPCtocpc','cpc-vue1')
}
10.1.9 model
@Model("changeCPCtocpc", { type: String }) private vmodel!: any;
model:{
event:"changeCPCtocpc",
prop: vmodel
}