来源:https://www.bilibili.com/video/BV1NR4y1x7Ab
TypeScript 的介绍
TypeScript是一种由微软开发的开源、跨平台的编程语言。它是JavaScript的超集,最终会被编译为JavaScript代码。
TypeScript 的特点
- 始于JavaScript,归于JavaScript
TypeScript 可以编译出纯净、 简洁的 JavaScript 代码,并且可以运行在任何浏览器上、Node.js 环境中和任何支持 ECMAScript 3(或更高版本)的JavaScript 引擎中。
- 强大的类型系统
类型系统允许 JavaScript 开发者在开发 JavaScript 应用程序时使用高效的开发工具和常用操作比如静态检查和代码重构。
- 先进的 JavaScript
TypeScript 提供最新的和不断发展的 JavaScript 特性,包括那些来自 2015 年的 ECMAScript 和未来的提案中的特性,比如异步功能和 Decorators,以帮助建立健壮的组件。
安装 TypeScript
npm install -g typescript
tsc -v # 检查是否安装成功
编译TypeScript程序
手动编译
tsc helloworld.ts
VsCode自动编译
- 生成配置文件tsconfig.json
tsc --init
- 修改tsconfig.json配置
"outDir": "./js",
"strict": false,
- 启动监视任务
终端 -> 运行任务 -> 监视tsconfig.json
TypeScript常用语法
基础类型
布尔
let flag: boolean = true;
数字
let num0: number = 10 //十进制
let num1: number = 0b10 //二进制
let num2: number = 0o12 //八进制
let num3: number = 0xa // 十六进制
字符串
可以用单引号或者双引号
let str: string = "haha"
undefined和null
TypeScript 里,undefined 和 null 两者各自有自己的类型分别叫做 undefined 和 null。 它们的本身的类型用处不是很大。
默认情况下 null 和 undefined 是所有类型的子类型。 就是说你可以把 null 和 undefined 赋值给 number 类型的变量。
let u: undefined = undefined
let n: null = null
数组
let list0: number[] = [1, 2, 3]
let list1: Array<number> = [1, 2, 3]
元组
let t: [string, number] = ["hello", 1]
枚举
enum Color {
Red,
Green,
Blue,
} //枚举数值默认从0开始一次递增
let color: Color = Color.Green
any
当我们不希望类型检查器对某些值进行检查而是直接让它们通过编译阶段的检查时, 可以使用 any 类型来标记这些变量。
let a: any = 4
a = "world"
void
某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void。
声明一个 void 类型的变量没有什么大用,因为你只能为它赋予 undefined 和 null。
function f(): void {
console.log("f")
}
let v: void = undefined
object
object 表示非原始类型,也就是除 number,string,boolean之外的类型。
联合类型
表示取值可以为多种类型中的一种
function f(x: number | string): string {
return x.toString()
}
类型断言
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式。 其一是“尖括号”语法, 另一个为 as 语法。
function getLength(x: number | string) {
if ((<string>x).length) {
return (x as string).length
} else {
return x.toString().length
}
}
类型推断
TS会在没有明确的指定类型的时候推测出一个类型
- 定义变量时赋值了, 推断为对应的类型
- 定义变量时没有赋值, 推断为any类型
let a = 123 //number
let b //any
b = 123
b = "hello"
接口
interface IPerson {
readonly id: number //只读
name: string //必有
age: number
sez?: string //可选
(source: string, subString: string): boolean //函数
}
类
class A {
// 属性
message: string
// 构造方法
constructor (message: string) {
this.message = message
}
// 普通方法
greet(): string{
return "hello" + this.message
}
}
const a = new A("world")
console.log(a.greet())
继承
- 一个类可以实现多个接口,implements
- 一个接口可以继承多个接口,extends
interface Alarm {
alert(): any;
}
interafce CarAlarm extends Alarm{
}
class Car implements CarAlarm {
alert() {
console.log("Car")
}
}
class Ship extends Car {
alert() {
console.log("Ship")
}
}
修饰符
public
成员都默认为 public。
private
当成员被标记成 private 时,它就不能在声明它的类的外部访问。
protected
protected 修饰符与private修饰符的行为很相似,但有一点不同,protected成员在派生类中仍然可以访问。
readonly
可以使用 readonly 关键字将属性设置为只读的。只读属性必须在声明时或构造函数里被初始化。
参数属性
参数属性可以方便地让我们在一个地方定义并初始化一个成员。
class Person {
constructor(readonly name: string){
}
}
const p = new Person("jack")
console.log(p.name)
参数属性通过给构造函数参数前面添加一个访问限定符来声明。使用 private 限定一个参数属性会声明并初始化一个私有成员;对于 public 和 protected 来说也是一样。
存取器
class Person{
firstName: string = "A"
lastName: string = "B"
get fullName() {
return this.firstName + "_" + this.lastName
}
set fullName(value) {
const names = value.split("_")
this.firstName = names[0]
this.lastName = names[1]
}
}
const p = new Person()
console.log(p.fullName)
p.fullName = "C_D"
静态属性
静态属性时类对象的属性,而非静态属性,是类的实例对象的属性
class Person{
name1: string = "A"
static name2: string = "B"
}
console.log(Person.name2)
console.log(new Person().name1)
抽象类
抽象类做为其它派生类的基类使用。它们不能被实例化。不同于接口,抽象类可以包含成员的实现细节。
abstract class Animal{
abstract cry()
run() {
console.log("run")
}
}
class Dog extends Animal {
cry() {
console.log("dog cry")
}
}
const dog = new Dog()
dog.cry()
dog.run()
函数
//命名函数
function add(x number, y number): number {
return x + y
}
//匿名函数
let add2 = function(x number, y number): number{
return x + y
}
//函数类型
let add3: (x: number, y: number) => number =
function(x: number, y: number): number {
return x + y
}
可选参数和默认参数
在TypeScript 里我们可以在参数名旁使用 ? 实现可选参数的功能。
也可以为参数提供一个默认值当用户没有传递这个参数或传递的值是 undefined 时,使用该默认值。
function buildName(firstName: string='A', lastName?: string): string {
if (lastName) {
return firstName + '-' + lastName
} else {
return firstName
}
}
剩余参数
可以把所有参数收集到一个变量里:剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。 编译器创建参数数组,名字是你在省略号( ...)后面给定的名字,你可以在函数体内使用这个数组。
function info(x: string, ...args: string[]) {
console.log(x, args)
}
函数重载
数名相同, 而形参不同的多个函数
在JS中, 由于弱类型的特点和形参与实参可以不匹配, 是没有函数重载这一说的 但在TS中, 与其它面向对象的语言(如Java)就存在此语法。
// 重载函数声明
function add (x: string, y: string): string
function add (x: number, y: number): number
// 定义函数实现
function add(x: string | number, y: string | number): string | number {
// 在实现上我们要注意严格判断两个参数的类型是否相等,而不能简单的写一个 x + y
if (typeof x === 'string' && typeof y === 'string') {
return x + y
} else if (typeof x === 'number' && typeof y === 'number') {
return x + y
}
}
这并不是真正意义上的函数重载。
泛型
指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定具体类型的一种特性。
泛型函数
function swap <K, V> (a: K, b: V): [K, V] {
return [a, b]
}
const result = swap<string, number>('abc', 123)
泛型接口
interface IbaseCRUD <T> {
data: T[]
add: (t: T) => void
}
class User {
id?: number;
name: string;
age: number;
constructor (name, age) {
this.name = name
this.age = age
}
}
class UserCRUD implements IbaseCRUD <User> {
data: User[] = []
add(user: User): void {
user = {...user, id: Date.now()}
this.data.push(user)
}
getById(id: number): User {
return this.data.find(item => item.id===id)
}
}
泛型类
在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型
class GenericNumber<T> {
zeroValue: T
add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
return x + y
}
泛型约束
interface Lengthwise {
length: number;
}
// 指定泛型约束
function fn2 <T extends Lengthwise>(x: T): void {
console.log(x.length)
}
声明文件
当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。
假如我们想使用第三方库 jQuery,一种常见的方式是在 html 中通过 <script> 标签引入 jQuery,然后就可以使用全局变量 $ 或 jQuery 了。
但是在 ts 中,编译器并不知道 $ 或 jQuery 是什么东西。这时,我们需要使用 declare var 来定义它的类型
declare var jQuery: (selector: string) => any;
jQuery('#foo');
一般声明文件都会单独写成一个 xxx.d.ts 文件很多的第三方库都定义了对应的声明文件库, 库文件名一般为 @types/xxx。有的第三库在下载时就会自动下载对应的声明文件库(比如: webpack),有的可能需要单独下载(比如jQuery/react)
//下载jquery的声明文件
npm install @types/jquery --save-dev
内置对象
JavaScript 中有很多内置对象,它们可以直接在 TypeScript 中当做定义好了的类型。
内置对象是指根据标准在全局作用域(Global)上存在的对象。这里的标准是指 ECMAScript 和其他环境(比如 DOM)的标准。
- ECMAScript 的内置对象
Boolean
Number
String
Date
RegExp
Error
- BOM 和 DOM 的内置对象
Window
Document
HTMLElement
DocumentFragment
Event
NodeList