TypeScript学习笔记

TypeScript 是 Javascript 的超集,遵循最新的 ES6、ES5 规范。TypeScript 扩展了 JavaScript 的语法。
安装

npm install -g typescript

浏览器不能直接执行ts代码,可以通过以下命令转换成js代码。

tsc index.ts
TypeScript开发工具 VS Code 自动编译.ts 文件
  1. 创建 tsconfig.json文件
tsc --init
  1. 编辑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
  1. export default一个文件只能出现一次
  2. 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
  • 属性装饰器
    属性装饰器表达式会在运行时当作函数被调用,传入下列两个参数:
  1. 对于静态属性(static声明)来说是类的构造函数,对于实例属性是类的原型对象
  2. 属性的名字
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个参数:
  1. 对于静态属性来说是类的构造函数,对于实例属性是类的原型对象
  2. 属性的名字
  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个参数:
  1. 对于静态属性来说是类的构造函数,对于实例属性是类的原型对象
  2. 方法的名字
  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)
装饰器执行顺序

属性->方法->方法参数->类
如果有多个同样的装饰器,它会先执行后面的

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 简介 TypeScript 是 JavaScript 的一个超集,主要提供了 类型系统 和对 ES6 的支持,由 ...
    MrWelson阅读 15,874评论 3 21
  • 重点: 解构与指示类型有时很像 关于类的部分 有些时候会有语法错误,比如不能这样或那样调用。这其实都是ts的语法要...
    苦苦修行阅读 280评论 0 0
  • 一、数据类型 ts的数据类型几乎与js一致 提供了数字(number)、字符串(string)、结构体(Objec...
    中華田園雞阅读 362评论 0 0
  • TS 安装 创建tsconfig.json文件 基本数据类型 布尔类型 boolen 数字类型 number 字符...
    情有千千节阅读 306评论 0 1
  • 2019年8月10日下午15:00,在重庆璧山黛山大道,一个楼盘搞促销,整了一个马戏嘉年华。室外温度近40℃,搭的...
    澎湃简报阅读 120评论 0 0