1. 前言
TypeScript是Javascript的超集,支持ES6标准,支持类型系统以及类型推断,支持运行在任何浏览器,Node.js环境中
2. 环境配置
- 安装node
- 安装typescript
-
npm install typescript
- 安装ts-node
-
npm install ts-node
全部安装完成之后运行typescript文件可以使用ts-node xxx.ts
相关命令
- 编译ts文件:
tsc xxx.ts
- ts初始化配置文件:
tsc -init
在ts文件中使用require的时候需要先安装**@types/node**
3. 变量声明
TypeScript声明变量的时候可以同时声明变量的类型, 但是这并不是必须的,如果没有进行指定,typescript会自动的进行类型推断
定义变量的时候我们最好使用的是 let
或者 const
,尽量不要使用 var
1. 基本元素定义
let flag:boolean = true // 声明变量的同时指定初值
let flag:boolean // 声明变量
let sentence:string = `Are you ok? => ${flag}` // 使用 ``进行字符串的声明,可以使用${varible}进行插值
常用的类型有: boolean
, string
, number
, any
any可以指任何类型
2. 数组元素定义
有两种方式可以进行数组元素的定义
- 将类型后面加上
[]
表示数组即可 - 使用数组泛型,
Array<元素类型>
let list:number[] = [1,2,3]
let list:Array<number> = [1,2,3]
如果在变量初始化阶段已经声明了该变量的类型,那么如果在之后的代码中赋值为其他类型的变量会编译报错
对于一个没有声明类型的变量,我们可以使用类型断言来指定变量的类型,而不需要typescript自动推断,有两种方式进行断言:
- 使用尖括号进行断言
- 使用关键词
as
进行断言
let varible:any = "this"
let len:number = (<string>varible).length // 指定
// or
let len:number = (varible as string).length
3. 同时约束多个变量类型
如果一个变量可能有多种类型,可以使用:
let arg:number|string;
4. 泛型
1. 泛型函数
定义格式:
function hello<T>(arg:T):T{
return arg;
}
最后的T是返回类型
使用方式
- 使用尖括号
<>
- 使用自动类型推断
let output = hello<string>("Hello world"); //
// or
let output = hello("Hello world"); // 自动推断
2. 泛型变量
比如在下方函数中, 如果使用length去获取arg的长度时,会报错,尽管在T为string等类型的时候成立,但是我们在没有进行类型判断之前应该将其视为任意类型,使用其属性或者对应的相关函数的时候一定要有对应的属性
// 错误类型
function hello<T>(arg:T):T{
console.log(arg.length);
return arg;
}
// 可以使用该类方式
function hello<T>(arg:T[]):T[]{
console.log(arg.length);
return arg;
}
3. 枚举
typescript支持数字枚举和字符串枚举
1. 数字枚举
enum num{
One=1,
Two,
Three,
Four
}
当指定One=1时,之后的元素值依次递增,如果不指定,则One=0
如果某个元素被指定成不是数字类型的数组,那么跟随其后的元素必须要指定初始值(枚举中的值必须是确定的)
enum num{
First="",
Three=1,// 必须进行指定
Four // 可以不指定,默认为2=1+1
}
2. 字符串枚举
在一个字符串枚举中,所有的成员都必须是字符串
enum num{
First="first",
Second="second"
}
3. 反向映射
比如定义下方的枚举:
enum num{
A
}
const a = num.A; // 可以获取A对应的值
const key = num[a]; // 根据值获取key值
4. symbol
通过 Symbol
构造函数创建
const s = Symbol("hello world");
const s1 = Symbol("hello");
const s2 = Symbol("hello");
console.log(s1===s2); // -> false
通过同样的方式生成两个symbol也不同,因为symbol值是唯一的
5. iterator和generator
1. iterator
for..of
和 for..in
都可以迭代一个数组,最大的区别在于:for..in
迭代的是对象的键,而 for..of
迭代的是对象的值
let array:number[] = [1,2,3]
//输出 1 2 3
for(let i of array){
console.log(i)
}
//输出 0 1 2
for (let i in array){
console.log(i)
}
2. generator
其实下面的这些代码在本机运行的时候一直报错,调用next函数不成功
function *
可以用来创建一个generator函数(懒迭代器),调用generator函数会返回一个generator对象,可以使用next,return 和throw函数
function* infiniteList(){
let i = 0;
while(true){
yield i++;
}
}
let iterator = infiniteList();
while(true){
console.log(iterator.next());
}
- generator 对象只会在调用next函数的时候才会执行
- 函数在执行到yield语句时会暂停并返回
yield
的值 - 函数在next被调用时继续恢复执行
可以使用next函数对内传值
function* generator(){
const who = yield;
console.log("hello "+who);
}
const iterator = generator();
console.log(iterator.next()); // {value:undefined, done: false}
console.log(iterator.next("world")); // hello world {value:undefined, done: true}
使用throw处理迭代器内部的报错
function* generator(){
try{
yield 1;
}catch(error){
console.log(error.message)
}
}
const iterator = generator();
iterator.next()
iterator.throw(new Error("something")); // 迭代器中使用异常处理的部分
5. TypeScript中的高级类型
1. interface
在需要对传参变量进行约束的时候,可以使用interface
interface ArgType {
a:number,
b:string
}
// 下面的这种方式需要arg包含ArgType中所有的属性
function test(arg:ArgType){
...
}
将属性变成可选的,使用 ?
interface ArgType2{
a?:number, // 这个属性可以指定,也可以不指定
b:string
}
设置属性名为只读不可修改
interface Person{
readonly name:string,
readonly age:number
}
2. 类型别名
使用type关键字可以用来描述类型变量
type Age = number
6. 函数
1. 定义函数
和js中定义函数类似,可以对变量和返回值添加类型,返回值通常可以省略类型限制。
function add(x:number, y:number):number{
return x+y;
}
2. 参数
在typescript中每个函数的参数都必须有值,可以传入undefined或者null,如果多一个或者少一个编译都不会通过,如果参数是可选的,我们可以使用
?
function add(x:number, y:number, z?:number){
if(z){
return x+y+z;
}else{
return x+y;
}
}
console.log(add(1,2))
console.log(add(1,2,3))
- 我们也可以提供默认的参数
但是默认参数可以不用放在最后,如果不在最后,那么必须使用undefined或者null进行占位
function fun(x:number, y:number, flag=true){
if(flag){
return x+y;
}else{
return x-y;
}
}
- 不定参数
可以使用 ....args
形式进行接收
在js中可以使用arguments来访问所有的参数
function add(...list:number[]){
return list.map(x=> x**2)
}
console.log(add(1,2,3))
3. 回调函数
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
即在另一个函数执行完之后执行的一个特殊函数,类似一个钩子,可以在一个函数中的某个时期执行
function hello(x: number, callback: (error: string) => void) {
if (x > 100) {
callback("greater than 100");
} else if (x < 10) {
callback("less than 10");
}
}
// 参数中的函数就是回调函数,回调函数的参数就是callback中对应的参数
hello(1, function (error) {
console.log(error + " hello");
});
4. 异步
使用 Promise
对象可以进行异步任务,使用可以见/Vue/下介绍
-
async
和await
在await关键字出暂停执行代码,等待结果返回,返回后继续执行代码
async是声明一个异步执行的函数之类的
async function fetchUser(id:number){
try{
return await findUser(id);
}catch(error){
console.log('error')
}
}
7. 接口与类
1. 接口
TypeScript 的核心原则是对值所具有的结构类型进行检查,接口的迪是为了这些类型命名,以及代码定义契约
interface不仅可用于描述对象的结构,还可以用作接口的关键字
interface Props{
width: number,
height: number
}
// 类型检查器
function area(data:Props){
console.log(data.width*data.height)
}
const data:Props = {
width: 100,
height: 100
}
area(data)
- 可选属性 -->
?
interface Props{
width?:number,
height: number
}
- 只读属性 -->
readonly
interface Props {
readonly width: number;
readonly height: number;
}
readonly用于属性的定义限制,const用于变量的限制
接口对象也可以函数的参数进行类型检查
interface SearchFunction {
(source: string, subString: string): boolean;
}
let search: SearchFunction;
// 如果search不满足interface中的类型,则编译不会通过
search = function (source: string, subString: string) {
let result = source.search(subString);
return result > -1;
};
// 同时,参数名不一定一致,但是类型必须一一对应
search = function (src: string, sub: string) {
let result = src.search(sub);
return result > -1;
};
还可以对interface类型的变量定义索引
interface StringArray{
// index -> 指定索引的方式
// string -> 指定返回的类型
[index: number]:string
// 如果设置成 readonly [index: number]:string 那么不能由索引进行赋值
// 而且在这个接口中的属性要保持和返回值类型string一样
// length:number x
// name:string √
}
let array:StringArray = ["first", "second"]
console.log(array[0])
- 接口的继承
接口继承相当于将成员复制到另一个接口里面,同时支持
多继承
interface Shape{
color: string
}
interface Square extends Shape{
sideLength:number
}
let square:Square = {
color: "green",
sideLength: 10
}
2. 类
1. 定义
class Person {
age: number; // 属性
name: string;
// 构造函数
constructor(name: string, age: number) {
this.age = age;
this.name = name;
}
// 类方法
info() {
return this.name + "is " + this.age + " years old";
}
}
// 创建一个对象
let person = new Person("Edgar", 20);
console.log(person.info());
2. 实现接口
interface ClockInterface{
currentTime:Date; // 属性
setTime(d:Date); // 方法,在类中需要实现
}
class Clock implements ClockInterface{
currentTime: Date;
setTime(d:Date){
this.currentTime = d;
}
constructor(hour: number, minute:number){};
}
3. 继承
class Animal{
name: string;
constructor(name:string){
this.name = name;
}
info(){
return this.name;
}
}
class Dog extends Animal{
constructor(name:string){
super(name);// 调用父类的构造函数
}
bark(){
console.log("Woof!")
}
}
let dog = new Dog("xiao wang");
console.log(dog.info());
dog.bark();
- 对属性设置
getter
setter
class Animal {
private _name: string;
constructor(name: string) {
this._name = name;
}
get name(): string {
return this._name;
}
set name(name: string) {
this._name = name;
}
}
let dog = new Animal("Dog");
dog.name = "nihao";
console.log(dog.name);
但是需要注意:
- 需要将编译器设置为输出ECMAScript5或者更高
- 只有get没有set的属性自动推断为readonly
- 设置属性为只读属性
只读属性必须在声明的时候进行初始化或者在构造函数中进行初始化
class octopus{
readonly name:string;
readonly numberOfLegs:number = 8; // 定义的时候进行初始化
constructor(name:string){
this.name = name // 构造函数中进行初始化
}
}
- 抽象类
使用 abstract
abstract class Animal { // 抽象类
private _name: string;
constructor(name: string) {
this._name = name;
}
get name(): string {
return this._name;
}
set name(name: string) {
this._name = name;
}
abstract bark():void; // 抽象函数
}
8. 命名空间和模块
1. 命名空间
1. 单文件命名空间
将所有命名放在一个命名空间下
// 所有的放在一个命名空间Validation下
namespace Validation{
export interface StringValidator{
match(s:string):boolean;
}
const letterRegex = /^[A-Za-z]+$/
export class LetterValidator implements StringValidator{
match(s:string){
return letterRegex.test(s)
}
}
}
let string = "hello"
let validator = new Validation.LetterValidator();
console.log(validator.match(string))
2. 多文件命名空间
将代码分割到多个同样的命名空间的不同文件中,虽然在不同的文件中,但是仍然属于同一个命名空间
// Validation.ts
namespace Validation {
export interface StringValidator {
match(s: string): boolean;
}
}
// LetterValidator.ts
namespace Validation {
const letterRegex = /^[A-Za-z]+$/;
export class LetterValidator implements StringValidator {
match(s: string) {
return letterRegex.test(s);
}
}
}
3. 别名
简化命名空间的操作,可以使用import q=x.y.z自定义名称
namespace Shape{
export namespace Polygons{
export class Triangle{}
export class Square{}
}
}
// 别名
import polygons = Shape.Polygons;
2. 模块
- 导出
- 任何声明(比如变量、函数、类、接口、类型别名等)都可以通过
export
关键字进行导出
export interface Hello{
// ...
}
export const PI=3.1415
export class Animal{
// ...
}
- 导出的时候进行重命名
class Animal{
// ...
}
export {Animal}; // 直接导出
export {Animal as A}; // 重命名导出
- 重新导出
可以将所有的内容统一导出
// index.ts
export * from 'xxx_0'
export * from 'xxx_1'
- 导入
- 导入模块中的一个内容
import {LetterValidator} from "xxx"
- 导入并重命名
import {LetterValidator as LV} from "xxx"
// 导入所有的模块到一个变量
import * as ALL from 'xxx' // 全部模块导入到变量ALL中
let xx = ALL.xxx // 使用ALL中的xxx定义变量
- 没有export的模块导入
import 'xxx'
- 默认导出
用 default
进行标记,且每个模块只有一个默认导出. 类和函数声明可以直接标记为默认导出
export default class Animal{
//...
}
// 导入的时候
// xxx-> 模块名
// xx-> 自定义名称
import xx from "xxx"