Typescript是什么?
- JavaScript的超集
- 静态类型检查
- 兼容ES6以上的语法
- 使代码更易读
- 编译前就能发现大量错误,减少低级错误的发生
安装和使用
安装 typescript
npm install -g typescript# 安装特定版本npm install -g typescript@版本号
查看版本
tsc -v
编译 ts 文件,生成 js 文件
tsc xxx.ts
ts-node
每次先 tsc 编译 ts 文件,再用 node 运行 js 文件太麻烦,使用 ts-node 可以将这两步合二为一
# 安装npm install -g ts-node# 运行ts-node xxx.ts

如果使用ts-node如上图报错,执行这条命令
npm install -D tslib @types/node
基础类型
// 基础类型let isDone: boolean = falselet age: number = 20let binaryNumber: number = 0b1111let firstName: string = 'hello'let u: undefined = undefinedlet n: null = null// 在typescript中, null和undefined都有各自的类型// undefined和null都可以是所有类型的子类型// undefined一般赋值给基础类型,null一般赋值给对象
Any和联合类型
// anylet notSure: any = 4notSure = truenotSure.myName// 联合类型 unionlet numberOrString: number | string = 100numberOrString = 'hello'
Array和Tuple
// Arraylet arr: number[] = [1, 2, 3, 4]// Tuplelet t: [string, number] = ['a', 1]
Interface
// Interface// 对对象的形状进行描述interface Person {readonly id: numbername: stringage: numberaddress?: string}const obj: Person = {id: 1,name: 'jack',age: 21,}
函数类型和类型推断
function add(x: number, y: number, z?: number): number {if (typeof z === 'number') {return x + y + z} else {return x + y}}const add2: (x: number, y: number, z?: number) => number = add
类
- 类( Class ): 定义了一切事物的抽象特点
- 对象( Object ): 类的实例
- 面向对象( OOP )三大特性: 封装、继承、多态
声明一个类
class Animal {name: stringconstructor(name: string) {this.name = name}run() {return `${this.name} is running`}}console.log(typeof Animal) // functionconst snake = new Animal('lily')console.log(snake.run())
转译成 JS, 可以看到 class 的本质是 function, 创建实例后,实例会拥有属性name,而 run 方法在原型上
var Animal = /** @class */ (function () {function Animal(name) {this.name = name;}Animal.prototype.run = function () {return this.name + " is running";};return Animal;}());console.log(typeof Animal); // functionvar snake = new Animal('lily');console.log(snake.run());
继承
新增方法
class Dog extends Animal {// 没重写constructor则使用父类的constructorbark() {return `${this.name} is barking`}}const xiaobao = new Dog('xiaobao')console.log(xiaobao.bark())
重写父类的constructor和方法
class Cat extends Animal {constructor(name: string) {super(name)console.log(this.name)}// 重新父类的方法run(): string {return 'Meow, ' + super.run() // super. 调用父类的方法}}const meme = new Cat('meme')console.log(meme.run())
修饰符
public 运行外部访问,为默认值
class Animal {public name: stringconstructor(name: string) {this.name = name}run() {return `${this.name} is running`}}
private 不允许任何人访问
protected 外部不能访问,但子类可以访问
readonly 外部能访问,但不能修改
静态属性和方法
静态属性和方法是直接在类本身上的,不需要新建实例就能访问
class Animal {static categories: string[] = ['mammal', 'bird']static isAnimal(a) {return a instanceof Animal}name: stringconstructor(name: string) {this.name = name}run() {return `${this.name} is running`}}console.log(Animal.categories)const snake = new Animal('lily')console.log(Animal.isAnimal(snake))
类和接口
如果两个类都有相同的方法,但又不好通过继承来定义,则可以使用interface
interface Radio {swicthRadio(): void}interface Battery {checkBatteryStatus(): void}class Car implements Radio {color: stringswicthRadio(): void {}}class Cellphone implements Radio, Battery {brand: stringswicthRadio(): void {}checkBatteryStatus(): void {}}
interface也可以继承
interface Radio {swicthRadio(): void}// interface Battery {// checkBatteryStatus(): void// }interface RadioWithBattery extends Radio {checkBatteryStatus(): void}class Cellphone implements RadioWithBattery {brand: stringswicthRadio(): void {}checkBatteryStatus(): void {}}
type 也可以被 implements, interface 和 type 最大的区别是 interface 声明可以合并
type Radio = {swicthRadio(): void}interface RadioWithBattery extends Radio {checkBatteryStatus(): void}interface RadioWithBattery {meow(): void}class Car implements Radio {color: stringswicthRadio(): void {}}class Cellphone implements RadioWithBattery {brand: stringswicthRadio(): void {}checkBatteryStatus(): void {}meow(): void {}}
枚举 enum
常规用法
enum Directions {up,left,right,down,}console.log(Directions.up)
如果子项不赋值,则默认从0开始
// 编译成JS后var Directions;(function (Directions) {Directions[Directions["up"] = 0] = "up";Directions[Directions["left"] = 1] = "left";Directions[Directions["right"] = 2] = "right";Directions[Directions["down"] = 3] = "down";})(Directions || (Directions = {}));console.log(Directions.up);
如果第一项赋值为number, 则后面项依次递增
enum Directions {up = 100,left,right,down,}console.log(Directions.up)
// 编译成JS后var Directions;(function (Directions) {Directions[Directions["up"] = 100] = "up";Directions[Directions["left"] = 101] = "left";Directions[Directions["right"] = 102] = "right";Directions[Directions["down"] = 103] = "down";})(Directions || (Directions = {}));console.log(Directions.up);
第一项赋值非number, 则其他项也都要赋值
enum Directions {up = 'UP',left = 'LEFT',right = 'RIGHT',down = 'DOWN',}console.log(Directions.up)
常量枚举
const enum Directions {up = 'UP',left = 'LEFT',right = 'RIGHT',down = 'DOWN',}const value = 'UP'if (value === Directions.up) {console.log('go up')}
编译后会发现枚举的声明都被舍弃了,使用常量枚举可以提升性能
var value = 'UP';if (value === "UP" /* up */) {console.log('go up');}
泛型
基本用法
在声明一个函数时,我们不希望指定传入参数和返回值的具体类型,而是等实际使用时才知道具体的类型,且我们希望函数传入参数的类型和返回值的相同或具有关联,则我们应该使用泛型( generics )
function echo<T>(arg: T):T{return arg}
<>中的 T 称为类型变量, T 帮助我们捕获用户传入的类型,之后再使用这个类型
在使用函数的时候我们可以明确指定 T 的类型
const result = echo<string>('hello')
或者不指定 T 的类型,只传入参数, Typescript会根据参数推断 T 的类型( 推荐 )
const result = echo(123)
数组和元组
// 数组function len<T>(arr: T[]):T[] {console.log(arr.length)return arr}// 或者function len<T>(arr: Array<T>):Array<T> {console.log(arr.length)return arr}// 元组function swap<T, U>(tuple:[T, U]): [U, T]{return [tuple[1], tuple[0]]}const result = swap(['hello', 123])
约束泛型
如果我们希望传入的参数只要有length属性就行,而不是只能传入数组
在类型参数中使用extends就能解决这个问题
interface IWithLength {length: number}function echoWithArray<T extends IWithLength>(arg: T):T {console.log(arg.length)return arg}echoWithArray([1, 2, 3])echoWithArray({length: 10})echoWithArray('hello')
类和接口
在 class 和 interface 中也能使用泛型
T 就是一个类型接收器,等待用户传入真正的类型
在 class 中使用泛型
class Queue<T> {private data = []push(item: T) {return this.data.push(item)}pop(): T {return this.data.shift()}}const queue = new Queue<number>()queue.push(1)queue.push('str') // 报错console.log(queue.pop().toFixed())const queue2 = new Queue<string>()queue2.push('str')console.log(queue2.pop().length)
interface中使用泛型
interface KeyPair<T, U> {key: Tvalue: U}let kp1: KeyPair<number, string> = {key: 1, value: 'str'}let kp2: KeyPair<string, number> = {key: 'str', value: 123}
TypeScript中内置了很多的接口,比如 Array
let arr: Array<number> = [1, 2, 3]
interface 还可以用来定义函数类型
interface IPlus<T> {(a: T, b: T): T}function plus(a: number, b: number): number {return a + b}function connect(a: string, b: string): string {return a + b}const a: IPlus<number> = plusconst b: IPlus<string> = connect
类型别名和类型断言
使用 type 关键字定义类型别名( type aliases)
type PlusType = (x: number, y: number) => numberfunction sum(x: number, y: number): number {return x + y}const sum2: PlusType = sumtype NameResolver = () => stringtype NameOrResolver = string | NameResolverfunction getName(n: NameOrResolver): string {if (typeof n === 'string') {return n} else {return n()}}
使用 as 关键字类型断言,告诉TS我比你更懂这个变量
function getLength(input: string | number):number {const str = input as Stringif (str.length) {return str.length} else {const number = input as Numberreturn number.toString().length}}
类型断言还可以简写:
if((<string>input).length) {return (<string>input).length} else {return input.toString().length}
声明文件
参考文档
当使用第三方库时,因为没有类型声明,TS无法识别
我们可以自己添加声明,新建以 .d.ts 结尾的文件,在其中添加类型声明
// src/jQuery.d.tsdeclare var jQuery: (selector: string) => any;
// src/index.tsjQuery('#foo');
- declare var / let / const 声明全局变量
- declare function 声明全局方法
- declare class 声明全局类
- declare enum 声明全局枚举类型
- declare namespace 声明(含有子属性的)全局对象
- interface和type 声明全局类型
如果声明了还是无法识别,在TS的配置文件 tsconfig.json 中添加如下配置:
{"include": ["**/*"]}
大多数第三方库都有了官方的声明文件,只需要npm安装即可, 例如
npm install --save @types/jquery
