TypeSrcipt

330 阅读7分钟

是基于JavaScript扩展的一门语言,是JavaScript的超集

在JavaScript原有基础上增加了一套强大的类型系统和对ES6的支持,最终TypeSrcipt会被编译回JavaScript,任何一个JavaScript运行环境都支持TypeSrcipt

原始类型检查

在变量后加 : 类型

const a: string = 'string'
const b: number = 100
const c: boolean = true
const d: void = undefined
const e: undefined = undefined
const f: null = null

标准库

是内置对象所对应的声明,需要额外声明标准库在tsconfig.json文件里lib

{
    "compilerOptions": {
        "target": "es5",
        "lib": ["ES2015", "DOM"], // 声明es2015和dom
    }
}

设置中文错误提示

编译后错误提示 命令行:yarn tsc --locale zh-CN

vsc中文错误提示: setting -> typescript locale

Object类型

ts中Object类型是泛指所有非原始类型,Object对象类型应该用接口声明

const foo1: object = function () {} // 可以是函数
const foo2: object = {} // 可以是对象
const foo3: object = [] // 可以是数组
// 指定obj1对象里面 只能有foo和bar属性
// 不能多不能少 并且两个属性的类型要对应上
const obj1: { foo: number, bar: string } = { bar: '123', foo: 10 }

Array类型

const arr1: Array<number> = [1, 2, 3]
const arr2: number[] = [1, 2, 3]
function sum (...args: number[]) {
    // ts的检查节省参数类型检查
    return args.reduce((prev, current) => prev + current, 0)
}
// sum(1, '2', '3') 类型错误编译不通过sum(1, 2, 3)

元组类型

定义数组的元素类型和个数,多用于函数返回

// 元素个数和类型都要对应上
const tuple: [number, string, boolean] = [10, 'abc', true]
// 用法和普通数组一样
// const age = tuple[0]
// const name = tuple[1]
const [arg, name] = tuple

枚举类型

默认声明的枚举是会生成双向访问,就是可以通过键访问枚举值,也可以通过值来访问键
这样做会在编辑后的js中生成枚举双向访问的代码,如果不需要通过值访问枚举键 可以将枚举声明为常量枚举
// 常量枚举 枚举类型
// 前面加const
// 常量枚举只是做枚举值的替换
const enum PostStatus {
    // 成员值可以不指定 默认从0开始累加
    // 如果前一个成员有指定值 后面就从这个指定值累加
    Draft, // 默认0
    unpublished = 5,
    Published, // 6
    //    other = 'other',
    // 成员累加只能是数字
    other1 = 'other1'
}
const post = {
    title: 'Hello TypeScript',
    content: 'TypeScript is a typed superset of JavaScript',
    status: PostStatus.Draft
}
// 如果是普通枚举就可以实现通过枚举值找枚举键
// console.log(PostStatus[5]) // unpublished

函数类型

// 函数声明的例子
// 两种方式实现参数可选的
// 一个是 c?: number 这种方式还可以设置如果传了必须是数字
// 另一是设置参数默认值 c: number = 10
// ...args 可以接收剩下的任意参数
// 必须返回一个字符串
function func1 (a: number, b: number, c: number = 10, ...args: number[]): string {
    // return 10
    return 'func1'
}
// func1(100) // 至少两个两个参数
// 第三个参数可选 默认值10func1(100, 200)
// 第三个参数必须是数字// func1(100, 200 , 'ss')
func1(100, 200, 300, 400) // ...args 接收剩余参数
// 函数表达式
const func2: (a: number, b: number) => string = function(a: number, b: number): string {
    return 'func2'
}

任意类型

any表示任意类型,ts不会对any类型进行类型检查,所以any类型是不安全的,any主要是用于兼容旧的代码写法

function stringifyJSON(value: any) {
    // JONS.stringify可以接收任意类型 所以在ts里用any声明
    return JSON.stringify(value)
}

隐式类型推断

如果没有在变量声明时指定类型,ts会根据声明时的初始值来推断变量的类型,效果和指定类型一样,如果没有初始值就无法推断,认为是any类型

应该为每个变量指定类型,方便代码阅读

let age = 18 // 自动推断是 number
// age = 'tom' // 检查类型不对
let name // 没有初始值 推断不了 认为是any 不作类型检查
name = 20
name = 'tom'

类型断言

明确代码执行结果的变量类型,声明在typeScript上

const nums = [1,2,3,4]
const res = nums.find(i => i > 0)
const num1 = res as number // 进行类型断言
// const square = res * res  // typeScript 不确定res是数字有可能是undefined
const square = num1 * num1

接口

用于对象的约束,规定对象中要有哪些属性,这些属性是什么类型

// 接口规定实现这个接口的对象要有的成员和对应类型
// 规定个数和对应的类型
// 可选成员可以没有
// 动态成员外可以额外
// 只读成员不可以修改 只可以初始化
interface Post {
    title: string,
    content: string,
    viewCount: number,
    subTitle?: string, // 可选成员 value是undefined
    readonly summary: string
    // 动态成员用兼容其他成员
    [key: string]: string | number | undefined
}
function printPost (post: Post) {
    console.log(post.title)
    console.log(post.content)
}
const post: Post = ({
    title: 'title',
    content: 'content',
    viewCount: 10,
    // subTitle: 'subTitle', // 因为可选成员可以没有
    summary: 'summary',
    other: 'other' // 因为动态成员所以可以多
})
printPost(post)
// 只读成员不可以改
// post.summary = 'otherSummary'

是用来描述一类事物具有对象的抽象成员,ES6中用class来声明类,typeScript中对类进行了增强

  • 构造函数中赋值的成员 要在类中声明

  • 成员声明时可以指定类型

  • 可以指定只读成员,只能初始化 或者 构造器赋值

  • 可以对类的成员和构造器增加访问三种修饰符

  • private 私有成员 只能当前类访问

  • protected 保护成员 只能当前类和子类访问

  • public 公共成员 都可以访问

    class Person {
        // 可以对类的成员增加访问修饰符
        // private 私有成员 只能当前类访问
        // protected 保护成员 只能当前类和子类
        // public 公共成员 都可以访问
        // 默认成员就是public
        public name: string // 可以在声明时赋值初始值
        private age: number // 也可以在构造函数中赋值
        protected readonly gender: boolean = true
        // 构造函数也可以添加访问修饰符
        // 默认是public
        // protect构造函数 只能用于当前类和被继承
        constructor (name: string, age: number) {
            // 构造函数中赋值的成员 要在类中声明
            this.name = name
            this.age = age
        }    
    sayHi (msg: string): void {
            console.log(`hello, ${this.name}, ${msg}`)
            console.log(this.age) // 当前类可以访问
        }
    }
    class Student extends Person {
        // 私有构造函数 不能在外边创建实例
        private constructor(name: string, age: number) {
            super(name, age)
            // 子类可以访问父类保护成员
            console.log(this.gender)
             // 不能修改只读成员
            // this.gender = false
        }    
    static create(name: string, age: number) {
            return new Student(name, age)
        }
    }
    const tom = new Person('tom', 20)
    // console.log(tom.age) // 私有成员不能访问
    // console.log(tom.gender) // 保护成员不能访问
    // 私有构造函数 不能外边创建实例
    // const jack = new Student('jack', 20)
    const jack = Student.create('jack', 22)
    

类和接口

接口可以定义类具有哪些方法, 抽象概念规定类要具体哪些方法
接口的定义应该越细化越好,一个接口应该定义一种行为的一些方法,如果有多种行为 分开接口定义,不要耦合,这样接口可以灵活搭配
interface Eat {
    eat (food: string): void
}
interface Run {
    run (distance: string): void
}
class Person implements Eat,Run {
    eat (food: string):void {
        console.log(`人吃 ${food}`)
    }
    run (distance: string):void {
        console.log(`人走 ${distance}`)
    }
}
class Animal implements Eat,Run {
    eat (food: string):void {
        console.log(`动物吃 ${food}`)
    }
    run (distance: string):void {
        console.log(`动物走 ${distance}`)
    }}
class Car implements Run {
    run (distance: string):void {
        console.log(`汽车走 ${distance}`)
    }
}

抽象类

相比于接口,定义更细化一些,定义一些公共的类都有的特征

// 抽象只能被继承 不能创建实例
abstract class Animal {
    eat (food: string): void {
        console.log(`动物吃东西 ${food}`)
    }
    // 抽象方法
    abstract run (distance: number): void
}
class Dog extends Animal {
    // 继承抽象类 需要实现抽象方法
    run(distance: number): void {
        console.log(`狗在跑 ${distance}`)
    }
}
const d = new Dog()d.eat('食物') // 可以使用继承的方法
d.run(100) // 子类实现的抽象方法

泛型

将声明时无法确定的参数类型定义成泛型,到使用的时候,传入泛型的类型来指定参数的类型

// 使用泛型 T 来代表一个类型
// 这个类型要调用时传入来确定
function createArray<T> (length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}
// 调用时传入泛型 传入的泛型决定类型
const res1 = createArray<number>(3, 200)
const res2 = createArray<string>(3, 'aaa')