TypeScript 是 Javascript 的超集,遵循最新的 ES6、ES5 规范。TypeScript 扩展了 JavaScript 的语法。
安装
npm install -g typescript
浏览器不能直接执行ts代码,可以通过以下命令转换成js代码。
tsc index.ts
TypeScript开发工具 VS Code 自动编译.ts 文件
- 创建 tsconfig.json文件
tsc --init
- 编辑tsconfig.json文件
"outDir": "./js",
表示生成的js文件存放目录为当前目录下的js目录
3.监听入口文件变化
选择terminal->run task->tsc:watch - tsconfig.json
或者执行命令
tsc -w
入口文件默认是index.ts
数据类型
布尔类型(boolean)
数字类型(number)
字符串类型(string)
数组类型(array)
元组类型(tuple)
枚举类型(enum)
任意类型(any)
null和undefined
void类型
never类型
ts数据类型校验
以下代码在es5中正确,在ts中错误
var a=12
a="12"
ts代码必须明确指定数据类型
var flag: boolean = true
var num: number = 123
var str: string = "abc"
数组
ts定义数组有两种方式
// es5
// var arr = ['1', '2']
// 第一种
var arr: number[] = [1, 2, 3]
// 第二种
var arr2: Array<number> = [1, 2, 3]
元组类型
//元组类型(tuple)
var tup: [number, string] = [10, 'abc']
枚举类型
//枚举类型
enum Flag { success = 1, error = 2 }
let resultCode: Flag = Flag.success
console.log(resultCode)
//return 1
如果枚举类型的标识符没有赋值,则它的值为下标
enum Color { red, green, blue }
console.log(Color.red)
//return 0
any类型
//任意类型
var str1: any = 12
str1 = "a str"
any类型可用于声明dom节点类型
var box: any = document.getElementById("box")
box.style.color = "red"
undefined类型
//undefined
var str2: undefined
console.log(str2)
//return undefined
声明可空变量
var num2: number | undefined
num2 = 20
null类型
//null
var width: null
声明多种类型
var height: number | undefined | null
height = 50
void类型
typescript中的void表示没有任何类型,一般用于定义方法的时候方法没有返回值
function run(): void {
console.log('running...')
}
function sum(): number {
return 98 + 1
}
never类型
是其他类型(包括 null 和 undefined)的子类型,代表从不会出现的值,这意味着声明never的变量只能被never类型所赋值
var a: never
a = (() => {
throw new Error('err')
})()
函数
- 没有参数
function run(): string {
return "running..."
}
var fun = function (): number {
return 10
}
- 带参数
function getInfo(name: string, age: number): string {
return `name:${name},age:${age}`
}
var info = function (name: string, age: number): string {
return `name:${name},age:${age}`
}
//info('xiaobai',2)
- 可选参数
function getAge(age?: number): void {
if (age) {
console.log(age)
}
}
可选参数必须在最后面
- 默认参数
function getAge(age: number = 20): void {
console.log(age)
}
- 剩余参数
function sum(...result: number[]): number {
var sum = 0
for (var i = 0; i < result.length; i++) {
sum += result[i]
}
return sum
}
alert(sum(1, 2, 3, 4, 5, 6))
...result
将传递的数组赋值给result
function sum(a: number, b: number, ...result: number[]): number {
var sum = a + b
for (var i = 0; i < result.length; i++) {
sum += result[i]
}
return sum
}
alert(sum(1, 2, 3, 4, 5, 6))
传递的数组前两个分别赋值给a和b
- 重载
function getInfo(name: string): string;
function getInfo(age: number): string;
function getInfo(str: any): any {
if (typeof str === 'string') {
return '我叫:' + str;
} else {
return '我的年龄是' + str;
}
}
- ts匿名函数
var res = function(a:number,b:number) {
return a*b;
};
console.log(res(12,2))
- 箭头函数
var time = 0
var timer = setInterval(() => {
time += 1
console.log(time)
if (time > 59) {
clearInterval(timer)
}
}, 1000)
- ts箭头函数
let convertToCamelCase = (foo: string): string => {
let arr: string[] = foo.split('-')
for (let i = 1; i < arr.length; i++) {
arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].substr(1, arr[i].length - 1)
}
return arr.join('')
}
ES5面向对象
function Person() {
this.name = '李白'
this.age = 15
this.drink = function () {
console.log(this.name + '在喝酒')
}
}
Person.prototype.gender = "男"
Person.prototype.work = function () {
console.log(this.name + '在写诗')
}
var p = new Person
p.drink()
p.work()
静态方法
Person.walk = function () {
console.log("People can walk.")
}
//调用
Person.walk()
类继承
- 对象冒充实现继承
function Child() {
Person.call(this)
}
var child = new Child()
child.walk() //People can walk.
child.work() //报错
对象冒充可以继承构造函数的属性和方法,不能继承原型链上(.prototype)的属性和方法,也不能继承静态方法
- 原型链实现继承
function Child() { }
Child.prototype = new Person()
原型链继承既可以继承构造函数中的属性和方法,又可以实现原型链上的属性和方法,但不能继承静态方法
问题:实例化子类时不能给父类类传参
function Child(name, age) { }
Child.prototype = new Person()
var child = new Child("李白", 23)
//child.name undefined
- 原型链+对象冒充继承
function Person(name, age) {
this.name = name
this.age = age
this.drink = function () {
console.log(this.name + '在喝酒')
}
}
Person.prototype.gender = "男"
Person.prototype.work = function () {
console.log(this.name + '在写诗')
}
Person.walk = function () {
console.log("People can walk.")
}
function Child(name, age) {
Person.call(this, name, age)
}
Child.prototype = new Person()
//等价于Child.prototype = Person.prototype
var child = new Child("李白", 10)
child.drink() //李白在喝酒
child.work() //李白在写诗
Child.walk() //not a function
解决了原型链继承不能传参问题
TypeScript面向对象
ts定义类
class Person {
name: string
constructor(name: string) {
this.name = name
}
work(): void {
console.log(this.name + "在工作.")
}
getName(): string {
return this.name
}
setName(name: string): void {
this.name = name
}
}
var p = new Person("李白")
p.work()
ts继承
class Child extends Person {
constructor(name: string) {
super(name)
}
}
var c = new Child("小李白")
c.work()
super()
调用父类构造函数
子类定义与父类相同的方法时,会覆盖父类方法
属性修饰符
public、protected、private
如果属性未加修饰符,默认是public
public name:string
修饰符 | 访问方式 |
---|---|
public | 类外部和类里面、子类 |
protected | 类里面、子类 |
private | 类里面 |
ts静态属性和方法
class Person {
public name: string
static age: number = 12
constructor(name: string) {
this.name = name
}
work(): void {
console.log(this.name + "在工作.")
}
static walk() {
console.log("人会走路.")
}
}
var p = new Person("李白")
p.work()
//调用静态方法
Person.walk()
//打印静态属性
console.log(Person.age)
ts多态
多态:父类定义一个方法不去实现,让继承的子类去实现,每一个子类都有不同的表现
class Animal {
name: string
constructor(name: string) {
this.name = name
}
eat(): void {
console.log("eat something")
}
}
class Dog extends Animal {
constructor(name: string) {
super(name)
}
eat(): void {
console.log("dog eat meat.")
}
}
class Cat extends Animal {
constructor(name: string) {
super(name)
}
eat(): void {
console.log("cat eat fish.")
}
}
抽象类和抽象方法
typescript中实现的抽象类,它提供其他类继承的基类,不能直接被实例化,用abstract关键字定义抽象类和抽象方法,抽象类中的抽象方法不能包含具体的实现,并且必须在继承类中实现
abstract class Animal {
name: string
constructor(name: string) {
this.name = name
}
abstract eat(): any
}
class Dog extends Animal {
constructor(name: string) {
super(name)
}
eat(): void {
console.log("dog eat meat.")
}
}
ts接口
- 接口对函数参数约束
interface Fullname {
firstName: string
lastName: string
}
function getName(name: Fullname) {
console.log(name.firstName + '/' + name.lastName)
}
- 接口可选参数
interface Fullname {
firstName: string
lastName?: string
}
- 函数类型接口,约束函数参数和返回值
interface encrypt {
(key: string, value: string): string
}
let md5: encrypt = function (key: string, value: string): string {
return key + value
}
console.log(md5("key", "val"))
- 索引接口
// 约束数组,下标是number类型,值是string类型
interface UserArr {
[index: number]: string
}
let arr: UserArr = ["a", "b"]
//约束对象
interface UserObj {
[index: string]: string
}
let obj: UserObj = { name: 'abc', age: "12" }
index是对象索引或数组索引
- 类类型接口
interface Animal {
name: string
eat(foot: string): void
}
class Dog implements Animal {
name: string
constructor(name: string) {
this.name = name
}
eat(food: string) {
console.log(this.name + " eat " + food)
}
}
- 接口继承
interface Animal {
eat(food: string): void
}
interface Person extends Animal {
work(): void
}
class Programmer implements Person {
work(): void {
console.log("programmer can write program")
}
eat(food: string): void {
console.log('programmer eat ' + food)
}
}
ts泛型
泛型就是解决类、接口、方法的复用性,以及对不特定的数据类型的支持
- 泛型函数
function getData<T>(value: T): T {
return value
}
getData<number>(123)
getData<string>("123")
- 泛型类
class MinClass<T>{
public list: T[] = []
add(value: T): void {
this.list.push(value)
}
min(): T {
let minNum = this.list[0]
for (let i = 1; i < this.list.length; i++) {
if (minNum > this.list[i]) {
minNum = this.list[i]
}
}
return minNum
}
}
let m1 = new MinClass<number>()
m1.add(1)
m1.add(9)
console.log(m1.min())
- 类作为参数的泛型类
class MySQLDb<T>{
insert(data: T): boolean {
console.log(data)
return true
}
}
class Post {
title: string | undefined
desc: string | undefined
body: string | undefined
constructor(params: {
title: string | undefined,
desc?: string | undefined,
body?: string | undefined,
}) {
this.title = params.title
this.desc = params.desc
this.body = params.body
}
}
let a = new Post({ title: "wz" })
let Db = new MySQLDb<Post>()
Db.insert(a)
- 泛型接口
interface ConfigFn {
<T>(value: T): string
}
let getData: ConfigFn = function <T>(value: T): string {
return typeof value === "number" ? "number" : "other"
}
console.log(getData<string>("str1"))
定义泛型接口其他方式
interface ConfigFn<T> {
(value: T): string
}
function getData<T>(value: T): string {
return typeof value === "string" ? "string" : "other"
}
let myGetter: ConfigFn<string> = getData
console.log(myGetter("abc"))//string
console.log(getData<number>(12))//other
TypeScript模块
- export多个函数或属性
//modules/db.ts
export let dbUrl = "mongodb://localhost/test"
export function getData(): any[] {
return [{ name: '小黑', age: 28 }, { name: '小白', age: 20 }]
}
export函数或属性的其他写法
export {}
let dbUrl = "mongodb://localhost/test"
function getData(): any[] {
return [{ name: '小黑', age: 28 }, { name: '小白', age: 20 }]
}
export { dbUrl, getData }
import函数或属性
import { dbUrl, getData } from './modules/db'
console.log(getData())
console.log(dbUrl)
- export单个函数
function getData(): any[] {
return [{ name: '小黑', age: 28 }, { name: '小白', age: 20 }]
}
export default getData
-
export default
一个文件只能出现一次 -
export default
导出的模块导入时不必带{}
import getData from './modules/db'
console.log(getData())
- export 类
modules/db.ts
interface Db<T> {
insert(data: T): boolean
update(data: T, id: number): boolean
}
export class MySQLDb<T> implements Db<T>{
insert(data: T): boolean {
console.log(data)
return true
}
update(data: T, id: number): boolean {
console.log("updated")
return true
}
}
index.ts
import { MySQLDb } from "./modules/db"
class User {
username: string | undefined
password: string | undefined
constructor(name: string, pass: string) {
this.username = name
this.password = pass
}
}
let user = new User("abc", "123")
let userDAO = new MySQLDb<User>()
userDAO.insert(user)
- 重构代码
modules/db.ts
interface Db<T> {
insert(data: T): boolean
update(data: T, id: number): boolean
}
export class MySQLDb<T> implements Db<T>{
insert(data: T): boolean {
console.log(data)
return true
}
update(data: T, id: number): boolean {
console.log("updated")
return true
}
}
model/User.ts
import { MySQLDb } from "../modules/db"
class User {
username: string | undefined
password: string | undefined
constructor(name: string, pass: string) {
this.username = name
this.password = pass
}
}
let UserModel = new MySQLDb<User>()
export {
User, UserModel
}
index.ts
import { User, UserModel } from './model/User'
let user = new User("abc", "123")
UserModel.insert(user)
TypeScript命名空间
在代码量较大的情况下,为了避免各种变量命名相互冲突,可以将相似的功能的函数、类、接口等放置到命名空间内。
ts中命名空间用用花括号包裹起来,用export
对外暴露需要访问的对象。一个模块内可能有多个命名空间
namespace A {
export class Animal {
name: string | undefined
constructor(name: string) {
this.name = name
}
run() {
console.log(this.name + " running...")
}
}
}
namespace B {
let dog = new A.Animal("dog")
dog.run()
}
let cat = new A.Animal("cat")
cat.run()
- export 命名空间
modules/a.ts
export namespace A {
export class Animal {
name: string | undefined
constructor(name: string) {
this.name = name
}
run() {
console.log(this.name + " running...")
}
}
}
index.ts
import { A } from './modules/a'
namespace B {
let dog = new A.Animal("dog")
dog.run()
//dog running...
}
let cat = new A.Animal("cat")
cat.run()
//cat running...
TypeScript装饰器
装饰器:装饰器是一种特殊类型的声明,它能够被附加到类声明,方法,属性或参数上,可以修改类的行为。
通俗的讲装饰器就是一个方法,可以注入到类、方法、属性参数上来扩展类、属性、方法、参数的功能。
常见的装饰器有:类装饰器、属性装饰器、方法装饰器、参数装饰器
装饰器的写法:普通装饰器(无法传参)、装饰器工厂(可传参)
tsconfig.json
"compilerOptions": {
"experimentalDecorators": true,
}
- 不带参数的装饰器
@装饰器名
//类装饰器
//params为HttpClient类
function log(params: any) {
params.prototype.apiUrl = "https://api.example.com/open/i/ocr/passport"
params.prototype.print = function (): void {
console.log('print something')
}
}
@log
class HttpClient {
constructor() { }
}
let client: any = new HttpClient()
console.log(client.apiUrl)
client.print()
- 带参数的装饰器(装饰器工厂)
function log(params: string) {
//params是传递的参数,target是HttpClient类
return function (target: any) {
console.log(target)
console.log("传递的参数:" + params)
//传递的参数:hello
}
}
@log("hello")
class HttpClient {
constructor() { }
}
let client: any = new HttpClient()
- 装饰器修改构造函数、属性和方法
function log(target: any) {
console.log(target)
return class extends target {
//必须重载HttpClient所有属性和方法
apiUrl: any = "修改后的url"
getData() {
console.log(this.apiUrl)
}
}
}
@log
class HttpClient {
public apiUrl: string | undefined
constructor() {
this.apiUrl = "修改前的url"
}
getData() {
console.log(this.apiUrl)
}
}
let client: any = new HttpClient()
client.getData()
//修改后的url
- 属性装饰器
属性装饰器表达式会在运行时当作函数被调用,传入下列两个参数:
- 对于静态属性(static声明)来说是类的构造函数,对于实例属性是类的原型对象
- 属性的名字
function logProperty(params: any) {
//target为HttpClient类的原型对象,attr为apiUrl(属性名)
return function (target: any, attr: any) {
console.log(target)
console.log(attr)
//用传递的params修改实例对象的属性
target[attr] = params
}
}
class HttpClient {
@logProperty("http://www.example.com/")
public apiUrl: string | undefined
constructor() { }
getUrl() {
console.log(this.apiUrl)
}
}
let client: any = new HttpClient()
client.getUrl()
//http://www.example.com/
- 方法装饰器
它被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。
方法装饰会在运行时传入下列3个参数:
- 对于静态属性来说是类的构造函数,对于实例属性是类的原型对象
- 属性的名字
- 成员的属性描述符
替换用装饰器装饰的方法(原来的方法不会执行)
function get(params: any) {
//target为HttpClient类,methodName为方法名,desc为描述信息,desc.value为当前装饰器装饰的方法
return function (target: any, methodName: any, desc: any) {
// console.log(target)
// console.log(methodName)
// console.log(desc)
desc.value = function (...args: any[]) {
//将方法的参数转化成字符串类型
args = args.map((value) => {
return String(value)
})
console.log(args)
//["12", "121"]
}
}
}
class HttpClient {
public apiUrl: string | undefined
constructor() { }
@get("http://example.com/")
getUrl(...args: any[]) {
console.log(this.apiUrl)
}
}
let client = new HttpClient()
client.getUrl("12", '121')
修改用装饰器装饰的方法
function get(params: any) {
//target为HttpClient类,methodName为方法名,desc为描述信息,desc.value为当前装饰器装饰的方法
return function (target: any, methodName: any, desc: any) {
//保留原有方法
let oMethod = desc.value
desc.value = function (...args: any[]) {
//将方法的参数转化成字符串类型
args = args.map((value) => {
return String(value)
})
//调用原来方法
oMethod.apply(this, args)
}
}
}
class HttpClient {
public apiUrl: string | undefined
constructor() { }
@get("http://example.com/")
getUrl(...args: any[]) {
console.log(args)
}
}
let client = new HttpClient()
client.getUrl(12, '121')
//["12", "121"]
- 方法参数装饰器
参数装饰器表达式会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元素数据 ,传入下列3个参数:
- 对于静态属性来说是类的构造函数,对于实例属性是类的原型对象
- 方法的名字
- 参数在函数参数列表中的索引
function logParams(params: any) {
// target类名,methodName方法名,paramsIndex参数下标
return function (target: any, methodName: any, paramsIndex: any) {
console.log(target)
console.log(methodName)//方法名getUrl
console.log(paramsIndex)//参数下标0
console.log(params)//参数名称uid
}
}
class HttpClient {
public apiUrl: string | undefined
constructor() { }
//装饰参数
getUrl(@logParams('uid') uuid: any) {
console.log(uuid)//123
}
}
let client = new HttpClient()
client.getUrl(123)
装饰器执行顺序
属性->方法->方法参数->类
如果有多个同样的装饰器,它会先执行后面的