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 = false
let age: number = 20
let binaryNumber: number = 0b1111
let firstName: string = 'hello'
let u: undefined = undefined
let n: null = null
// 在typescript中, null和undefined都有各自的类型
// undefined和null都可以是所有类型的子类型
// undefined一般赋值给基础类型,null一般赋值给对象
Any和联合类型
// any
let notSure: any = 4
notSure = true
notSure.myName
// 联合类型 union
let numberOrString: number | string = 100
numberOrString = 'hello'
Array和Tuple
// Array
let arr: number[] = [1, 2, 3, 4]
// Tuple
let t: [string, number] = ['a', 1]
Interface
// Interface
// 对对象的形状进行描述
interface Person {
readonly id: number
name: string
age: number
address?: 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: string
constructor(name: string) {
this.name = name
}
run() {
return `${this.name} is running`
}
}
console.log(typeof Animal) // function
const 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); // function
var snake = new Animal('lily');
console.log(snake.run());
继承
新增方法
class Dog extends Animal {
// 没重写constructor则使用父类的constructor
bark() {
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: string
constructor(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: string
constructor(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: string
swicthRadio(): void {}
}
class Cellphone implements Radio, Battery {
brand: string
swicthRadio(): void {}
checkBatteryStatus(): void {}
}
interface也可以继承
interface Radio {
swicthRadio(): void
}
// interface Battery {
// checkBatteryStatus(): void
// }
interface RadioWithBattery extends Radio {
checkBatteryStatus(): void
}
class Cellphone implements RadioWithBattery {
brand: string
swicthRadio(): 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: string
swicthRadio(): void {}
}
class Cellphone implements RadioWithBattery {
brand: string
swicthRadio(): 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: T
value: 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> = plus
const b: IPlus<string> = connect
类型别名和类型断言
使用 type 关键字定义类型别名( type aliases)
type PlusType = (x: number, y: number) => number
function sum(x: number, y: number): number {
return x + y
}
const sum2: PlusType = sum
type NameResolver = () => string
type NameOrResolver = string | NameResolver
function 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 String
if (str.length) {
return str.length
} else {
const number = input as Number
return number.toString().length
}
}
类型断言还可以简写:
if((<string>input).length) {
return (<string>input).length
} else {
return input.toString().length
}
声明文件
参考文档
当使用第三方库时,因为没有类型声明,TS无法识别
我们可以自己添加声明,新建以 .d.ts 结尾的文件,在其中添加类型声明
// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;
// src/index.ts
jQuery('#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