TypeScript 是 JavaScript 的一个超集,它支持最新的 ES6 语法。TypeScript 可以编译成纯 JavaScript。
上手
通过 npm 或者 yarn 安装 typescript。生成配置文件,把 ts 文件编译成 js 文件,可以配置文件夹批量编译,也可以单个文件编译,单个文件编译不会启用配置文件。
yarn add typescript --devyarn tsc --inityarn tscyarn tsc ts文件相对路径
TypeScript 的语法与 JavaScript 十分接近,但是在配置选项开启了严格检查选项,TypeScript 就不能使用隐式类型。如果使用 vs code, 即使没有开启严格选项,vs code 也会提示 ts 文件的隐式类型警告。
// 可以完全按照 JavaScript 标准语法编写代码const hello = (name: any) => {console.log(`Hello, ${name}`)}hello('TypeScript')
原始数据类型
// 原始数据类型const a: string = 'foobar'const b: number = 100 // NaN Infinityconst c: boolean = true // false// 在非严格模式(strictNullChecks)下,// string, number, boolean 都可以为空// const d: string = null// const d: number = null// const d: boolean = nullconst e: void = undefinedconst f: null = nullconst g: undefined = undefined// Symbol 是 ES2015 标准中定义的成员,// 使用它的前提是必须确保有对应的 ES2015 标准库引用// 也就是 tsconfig.json 中的 lib 选项必须包含 ES2015const h: symbol = Symbol()// Promise// const error: string = 100
标准库声明
在配置文件中有一个选项 target,规定 ts 使用的 ES 标准,这个选项的值如果不是 “ES2015” 或以上的版本,Promise 等新特性就无法使用。如果因为一些原因不能更改 target 的值,要解决这个问题,那么可以启用另一个选项 lib,声明要额外使用的标准库。ES2015 和 DOM 一般是需要使用的。
{"compilerOptions": {"lib": ["ES2015", "DOM", "ES2017"]}}
作用域问题
默认文件中的成员会作为全局成员,多个文件中有相同成员就会出现冲突。使用立即调用函数表达式,或者使用 export 把文件当成一个模块,可以解决问题。
// 作用域问题// const a = 123 // 全局成员// 解决办法1: IIFE 提供独立作用域// (function () {// const a = 123// })()// 解决办法2: 在当前文件使用 export,也就是把当前文件变成一个模块// 模块有单独的作用域const a = 123export {}
Object 类型
Object 并不单指对象,而是指除原始类型以外的所有类型,包括对象,数组,函数,类等。
数组
// 数组类型export {} // 确保跟其它示例没有成员冲突// 数组类型的两种表示方式const arr1: Array<number> = [1, 2, 3]const arr2: number[] = [1, 2, 3]// 案例 -----------------------// 如果是 JS,需要判断是不是每个成员都是数字// 使用 TS,类型有保障,不用添加类型判断function sum (...args: number[]) {return args.reduce((prev, current) => prev + current, 0)}sum(1, 2, 3) // => 6
元组
元组是一种特殊的数据结构,它有明确的元素数量,元素具有明确的数据类型。
// 元组(Tuple)export {} // 确保跟其它示例没有成员冲突const tuple: [number, string] = [18, 'zce']// const age = tuple[0]// const name = tuple[1]const [age, name] = tuple// ---------------------const entries: [string, number][] = Object.entries({foo: 123,bar: 456})const [key, value] = entries[0]// key => foo, value => 123
枚举 enum
// 枚举(Enum)export {} // 确保跟其它示例没有成员冲突// 用对象模拟枚举// const PostStatus = {// Draft: 0,// Unpublished: 1,// Published: 2// }// 标准的数字枚举// enum PostStatus {// Draft = 0,// Unpublished = 1,// Published = 2// }// 数字枚举,枚举值自动基于前一个值自增// enum PostStatus {// Draft = 6,// Unpublished, // => 7// Published // => 8// }// 字符串枚举// enum PostStatus {// Draft = 'aaa',// Unpublished = 'bbb',// Published = 'ccc'// }// 常量枚举,不会侵入编译结果const enum PostStatus {Draft,Unpublished,Published}const post = {title: 'Hello TypeScript',content: 'TypeScript is a typed superset of JavaScript.',status: PostStatus.Draft // 3 // 1 // 0}// PostStatus[0] // => Draft
函数
TypeScript 的两种函数定义方式:函数声明和函数表达式。
// 函数类型export {} // 确保跟其它示例没有成员冲突function func1 (a: number, b: number = 10, ...rest: number[]): string {return 'func1'}func1(100, 200)func1(100)func1(100, 200, 300)// -----------------------------------------const func2: (a: number, b: number) => string = function (a: number, b: number): string {return 'func2'}
隐式类型推断
// 隐式类型推断export {} // 确保跟其它示例没有成员冲突let age = 18 // number// age = 'string' // 报错let foo // anyfoo = 100foo = 'string'// 建议为每个变量添加明确的类型标注
类型断言
告诉 TypeScript,该变量一定是某一类型。
// 类型断言export {} // 确保跟其它示例没有成员冲突// 假定这个 nums 来自一个明确的接口const nums = [110, 120, 119, 112]const res = nums.find(i => i > 0)const square = res * res // 报错,res 有可能是 undefined// 断言方式一const num1 = res as number// 断言方式二const num2 = <number>res // JSX 下不能使用
接口
interface 是一种规范,或者约定。它规定了一个对象应该有哪些属性或方法。
// 可选成员、只读成员、动态成员export {} // 确保跟其它示例没有成员冲突// -------------------------------------------interface Post {title: stringcontent: stringsubtitle?: string // 可选成员readonly summary: string // 只读成员,实现后不可修改}const hello: Post = {title: 'Hello TypeScript',content: 'A javascript superset',summary: 'A javascript'}// hello.summary = 'other'// ----------------------------------interface Cache {[prop: string]: string}const cache: Cache = {}cache.foo = 'value1'cache.bar = 'value2'
类
类用来描述一类具体对象的抽象成员。
private:加上这个修饰的属性和方法,只允许在自己本身这个类里访问,程序的任何其它地方都不能访问。
protected:受保护的,位于public和private中间,加上这个修饰的属性和方法,只能在子类(extends)和同包下的程序访问,别的的地方不能访问。
readonly 只能在属性声明或构造方法中赋值一次。
// 类的访问修饰符export {} // 确保跟其它示例没有成员冲突class Person {public name: string = 'init name' // 默认 publicprivate age: numberprotected readonly gender: booleanconstructor (name: string, age: number) {this.name = namethis.age = agethis.gender = true}sayHi (msg: string): void {console.log(`I am ${this.name}, ${msg}`)console.log(this.age)}}class Student extends Person {private constructor (name: string, age: number) {super(name, age) // 调用父类的构造方法console.log(this.gender)}static create (name: string, age: number) {return new Student(name, age)}}const tom = new Person('tom', 18)console.log(tom.name)// console.log(tom.age)// console.log(tom.gender)const jack = Student.create('jack', 18)
类与接口
接口相比类更加抽象一些,接口的属性不能初始化值,它的方法没有具体的实现,只是定义了这个接口有这一个方法。比如下面的代码,Eat、Run 接口,动物和人都会吃和跑,但是对于不同的生物来讲,吃和跑的方式不一样,方法的逻辑就不一样。
一个类可以实现多个接口。
// 类与接口export {} // 确保跟其它示例没有成员冲突interface Eat {eat (food: string): void}interface Run {run (distance: number): void}class Person implements Eat, Run {eat (food: string): void {console.log(`优雅的进餐: ${food}`)}run (distance: number) {console.log(`直立行走: ${distance}`)}}class Animal implements Eat, Run {eat (food: string): void {console.log(`呼噜呼噜的吃: ${food}`)}run (distance: number) {console.log(`爬行: ${distance}`)}}
抽象类
抽象类无法创建实例,但是它可以具体实现方法,接口不行。继承抽象类的子类需要实现用 abstract 修饰的方法或属性。
// 抽象类export {} // 确保跟其它示例没有成员冲突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)
泛型
泛型是定义函数、接口或类等时,不指定具体的类型,等到调用的时候再传递一个类型。泛型提高了通用性。
// 泛型export {} // 确保跟其它示例没有成员冲突function createNumberArray (length: number, value: number): number[] {const arr = Array<number>(length).fill(value)return arr}function createStringArray (length: number, value: string): string[] {const arr = Array<string>(length).fill(value)return arr}function createArray<T> (length: number, value: T): T[] {const arr = Array<T>(length).fill(value)return arr}// const res = createNumberArray(3, 100)// res => [100, 100, 100]const res = createArray<string>(3, 'foo')const res_1 = createArray<number>(4, 0)
