Featured image of post TypeScript 入门

TypeScript 入门

学习TypeScript基本语法,快速从JavaScript切换到TypeScript开发

注:该文为入门学习文档,更多内容前往 TypeScript中文网

TypeScript 简介

什么是 TypeScript

  1. 以 JavaScript 为基础构建的语言
  2. 兼容所有 JavaScript 代码
  3. 不能被 JS 解析器直接执行,需要先将 TS 编译成 JS
  4. 对 JavaScript 进行了丰富的扩展,适用于大型项目开发

TypeScript 增加了什么 ?

  1. 变量有类型,支持类型检测,更丰富的数据类型
  2. 支持所有 ES 特性,并有额外的特性
  3. 丰富的配置选项
  4. 区分大小写

安装使用 TypeScript

  1. 安装 node.js
  2. 命令行输入:npm i typescript -g
  3. 命令行输入:tsc 【*.ts文件】。将 TS文件编译成 JS文件

学习 TypeScript

类型声明

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* 声明变量,赋值 */
let a: number
a = 10
// a = 'sdsdsd' //error

/* 声明变量并赋值 */
let c: boolean = true
c = false
// c = 123 //error

/* 类型推断 */
let s = 'hello'
// s = 123 //error

/* 参数声明类型 */
let fn = function sum(a: number, b: number) {
	return a + b
}

/* 返回值声明类型 */
let fn2 = function sum2(a: number, b: number): number {
	return a + b
	// return 'hello' //error
}

TypeScript 数据类型

类型 例子 描述
number 123,12.3 任意数字
string “hello” 任意字符串
boolean true 布尔值
字面量 let a:“hello” | 123 变量的值就是字面量的值(不能修改)
any * 任意类型
unkonwn * 类型安全的any(不能直接赋值给其他变量)
void 空(undefined) 空值| undefined
never 没有值 不能是任何值
object {name: ‘Saly’} 任意 js 对象
array [1,2,3] 任意 js 数组
tuple [4,5] 固定长度和类型数组(新增)
enum enum{A,B} 枚举(新增)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/* any */
let any: any
let any2
any = 123
any = 'hello'
any2 = 123
any2 = 'hello'

/* unknown:可以赋值给 unkonwn 和 any,其他不行 */
let unknown: unknown
let unknown2: unknown
let str: string
unknown = 'hello'
unknown2 = unknown
any = unknown
// str = unknown //error
str = unknown as string
str = <string>unknown

/* never:一般用来处理报错 */
function error(): never {
	throw new Error('error!')
}

/* object:限制对象赋值*/
let obj1: { name: string; age?: number } //name必选,age可选
obj1 = { name: 'Saly', age: 18 }
let obj2: { [propName: string]: any } //对象属性名、数量任意
obj2 = { a: 123, b: 'hello' }

/* 限制函数参数和返回值 */
let fn3: (a: number, b: number) => number //参数必须2个number,且返回值是number

/* array */
let array1: string[] //字符串数组
let array2: Array<number> //数值数组

/* tuple */
let tuple1: [number, string] //必须2个元素且类型对应
tuple1 = [123, 'hello']

/* enum */
enum Gender {
	Male = 1,
	Female
}
let gender: Gender
gender = Gender.Male
console.log(Gender[1]) // Male
console.log(Gender[2]) // Female
console.log(Gender.Male) // 1
console.log(Gender[.Female) // 2

/* 自定义类型 */
type myType = 1 | 2 | 3 | 'hello'
let mytype: myType

配置选项

在文件根目录添加 tsconfig.json ,配置 TS 文件执行选项

注意:这个配置已废弃,现 JS 和 TS 统一为 jsconfig.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
{
	"include": [
		//编译src目录下所有目录的所有 .ts文件
		"./src/**/*"
	],
	"exclude": [
		//不编译src目录下所有目录的所有 .ts文件
		"./src/**/*"
	],
	"extends": [ //引入外部配置文件
	    "./src/config.json"
	],
	"files": [], //编译具体 .ts文件

	/* 编译配置 */
	"compilerOptions": {
		//被编译的 ES 的版本,默认 ES3
		"target": "ES6", //可接受 ES3/ES5/ES6/ES2015~ES2020/ESNEXT。忽略大小写
		// 指定要使用的模块化规范
		"module": "ES6", //可接受 none/commojs/amd/system/umd/es6/es2015/es2020/esnext。忽略大小写
		// 指定 ts 要使用哪些库,一般不改
		"lib": [],
		// 编译后文件所在目录
		"outDir": "./dist",
		// 所有编译到一个文件
		"outFile": "./app.js",
        // 是否编译 js文件
        "allowJs": false,
        // 检测js代码是否符合规范
        "checkJs": false,
        // 编译到 js文件,移除注释
        "removeComments": false,
        // 不生成 js文件
        "noEmit": false,
        // 发生错误不编译成js文件
        "noEmitOnError": false,
        // 设置编译的js文件是否严格模式(模块化自动进入严格模式)
        "alwaysStrict": false,
        // 不运行隐式类型的any
        "noImplicitAny": false,
        // 不允许不明确的this
        "noImplicitThis": false,
        // 严格检测空值
        "strictNullChecks": false,
        // 开启所有严格检查
        "strict": false
	}
}

面向对象

类(class)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {
	/* 属性 */
	name: string = 'Saly'
	age: number = 18
	static gender: string = 'female' // 静态属性
	readonly pets: Array<string> = ['cat'] // 只读属性

	/* 构造函数 */
	constructor(name: string, age: number) {
		this.name = name
		this.age = age
	}

	/* 方法 */
	sayHello() {
		console.log('hello')
	}

	// 静态方法
	static eat() {
		console.log('eat')
	}
}

继承(extends)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/* 继承父类所有属性和方法 */
class Student extends Person {
	major: string = '计算机'
	constructor(name: string, age: number, major: string) {
		super(name, age) // 如果有构造函数,必须调用父类构造函数
		this.major = major
	}

	// 重写父类方法
	sayHello() {
		// super.sayHello  //调用父类
		console.log(`${this.name}student say hello`) //模板引号
	}
}

抽象类(abstract)

1
2
3
4
5
// 抽象类只能被继承,不能实例化
abstract class Cat {
	// 抽象方法,没用方法体,必须被子类重写
	abstract sayHello(): void
}

接口(interface)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	// 接口用于限制类的结构
	interface MyInterface {
		name: string
		age: number

		// 接口中定义的方法都是抽象方法
		sayHello(): void
	}

	// 定义同名接口会组合在一起
	interface MyInterface {
		gender: string
	}

	class Teacher implements MyInterface {
		name: string
		age: number
		gender: string

		sayHello(): void {
			console.log('teacher say hello')
		}
	}

属性修饰符

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
	class Person {
		public _name: string // 可以在任意位置被访问(修改)
		protected _gender: string // 只能子类或类内部可以访问修改
		private _age: number // 只能类内部访问和修改

		// 可以通过调用属性的方式获取私有属性
		get age(): number {
			return this._age
		}

		// 可以通过调用属性的方式修改私有属性
		set age(value: number): void {
			this._age = value
		}
	}

泛型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
	// 定义一个到多个泛型
	function fn<T, K>(a: T, b: K): T {
		console.log(b)
		return a
	}
	fn<string, number>('hello', 123)

	// 指定泛型范围
	interface Type {
		lengh: number
	}
	// 泛型必须是Type的实现类(子类)
	function f2<T extends Type>(a: T) {
		return a.lengh
	}

装饰器

  • 本质是一个函数
  • 通过特定语法在特定位置调用 装饰器函数 对(类、属性、方法、参数等)进行扩展
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
*	定义一个装饰器
*	@params target:被装饰对象所属的类
*	@params name:被装饰对象的名称
* 	@params descriptor:描述器
*/
function log(target: Function, name: string, descriptor: PropertyDescriptor) {
    console.log(target, name, descriptor);
    // 提取原始方法
    const fn = descriptor.value;
    // 修改|扩展原始方法
    descriptor.value = (a: number, b: number) => {
		const result = fn(a,b);
        console.log('日志', { name, a, b, result });
        // 返回结果
        return result;
    }
};

class MyMath {
    @log
    static add(a: number, b: number) {
        return a+ b;
    }
}

MyMath.add(1, 2);

高级

联合类型和类型保护

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
interface Bird {
	isFly: boolean;
    sing: () => {};
}
interface Dog {
	isFly: boolean;
    bark: () => {};
}

// 通过 类型断言 保护
const trainAnimal = (animal: Bird | Dog) {
	 if(aniaml.isFly) {
		(animal as Bird).sing();
     } else {
         (animal as Dog).bark();
     }
}
// 通过 in 保护
const trainAnimal2 = (animal: Bird | Dog) {
	 if('sing' in aniaml) {
 		 anima.sing();
     } else {
         animal.bark();
     }
}
// 通过 typeof 保护
const add = (a: string | number, b: string | number) {
    if(typeof a === 'string' || typeof b === 'string')
        return parseInt(a) + parseInt(b)
    return a + b
}
// 通过 instanceof 保护:类型声明为一个对象
class NumberObj {
   count: number 
}
const add2 = (a: object | NumberObj, b: object | NumberObj) {
    if(a instanceof NumberObj && b instanceof NumberObj)
        return a.count + b.count
    return 0
}

命名空间 namespace

  1. 命名空间可以使得变量不会被全局暴露
  2. 可以通过export将命名空间内的变量等暴露,原理是挂载到命名空间对象上
  3. 一般用 ES6 的模块化导出导入
  • layout.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
namespace Layout {
    // 向外暴露 子命名空间
    export namespace subLayout {
        export class Test {}
    }
        
    // 向外暴露 接口
    export interface User {
        name: string;
    }
        
    // 向外暴露 类、变量等
    export class Header {}
    export class Sider {}
    export class Content {}
    export class Footer {}
}
  • page.ts
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/// <reference path='./layout.ts' />
namespace Home {
	export class Page {
        user: Layout.user = {
            name: 'Lisa';
        }
        constructor() {
			new Layout.Header();
            new Layout.Sider();
            new Layout.Content();
            new Layout.Footer();
        }
    }
}

*.d.ts类型文件声明

  • 例如:声明一个 jQuery 模块的类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
declare module 'jquery' {
	interface JqueryInstance {
		html: (html: string) => JqueryInstance;
    }
    // 混合类型,可以被声明多次
    function $(callback: () => void): void;
    function $(cssSelector: string): JqueryInstance;
    namespace $ {
        namespace fn {
			class init {}
        }
    }
    // 导出变量
    export = $
}

泛型+keyof

该用法根据入参类型,返回确定类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
interface Person {
	name: string;
    age: number;
    gender: string;
}

class Teacher {
    constructor(private info: Person) {}
    getInfo<T extends keyof Person>(key: T): Person[T] {
        return this.info[key]
    }
}

const teacher = new Teacher({
    name: 'Lisa',
    age: 18,
    gender: 'female'
})

const test = teacher.getInfo('name') // return string