--悄悄的开始--
1 简介
TypeScript 是 JavaScript 的类型的超集,可编译成纯 JavaScript。
TypeScript 为强类型语言,它的类型系统增加了代码的可维护性。
TypeScript 在编译阶段就可以发现大部分错误,比在运行时候发现错误更高效。
2 安装
npm install -g typescript
3 编译器
VSCode 等主流 IDE 均可。
4 第一个程序
新建文件,后缀为 firstDemo.ts:
function sayHello (name: string) {
return 'Hello, ' + name + '!'
}
let myname: string = 'World'
console.log(sayHello(myname))
在控制台执行命令:tsc firstDemo.ts
编译生成一个同名的 js 文件:
function sayHello(name) {
return 'Hello, ' + name + '!';
}
var myname = 'World';
console.log(sayHello(myname));
5 数据类型
① 布尔值 boolean
let flag: boolean = true
② 数值 number
let num1: number= 1000
let num2: number = 0b1010
③ 字符串 string
let str: string= 'Lucy'
④ 空值 void
function execute: void() {
// do somthing
}
⑤ 空值 null 和 undefined
let u: undefined = undefined
let n: null = null
⑥ 任意类型 any
包容任意类型数据:
let o: any = 10
o = 'ten'
console.log(o)
o.desc = 'number'
console.log(o.desc)
注意:定义变量时未指定类型,会被视为 any 类型。
⑦ 联合类型 |
let o: string|number = 10
o = 'ten'
console.log(o)
⑧ 接口 interface
interface Fruit { // 类似函数定义,但没有 ()
readonly code: string // 只读属性
name: string // 以分号间隔或不写任何符号,注意别写成逗号
count: number
color?: string // 可选属性
}
let orange: Fruit = {
code: 'ORANGE' // 只读属性只允许实例化时被赋值一次
name: '桔子',
count: 10
}
注意:接口一般首字母大写;实例属性多和少都不行,必须一致,可选属性除外;只读属性仅允许在实例化时被初始化一次。
⑨ 数组 [ ]
const arr1: number[] = [1,2,3,4,5,6,7,8,9]
const arr2: Array<number> = [1,2,3,4,5,6,7,8,9] // 利用泛型定义
interface DemoArray {
[index: number]: number
}
const arr3: DemoArray = [1,2,3,4,5,6,7,8,9] // 利用接口定义,不常用
const arr4: any = [100,'abc',{name:'array'}] // any 类型数组
⑩ 类数组(对象)
类数组对象即为有 length 属性和索引的对象,函数内置参数 arguments 就是类数组对象:
// 内置接口
interface IArguments {
[index: number]: any
length: number
callee: Function
}
function test(a: number, b: string, c: boolean) {
console.log(arguments)
}
test(100, 'xx', true)
6 函数
① 定义方式:
// 定义方式一,约定入参类型及返回值的类型
function test1(a: number, b: number, c: number): number {
return a + b + c
}
// 定义方式二,约定入参类型及返回值的类型
const test2 = function (a: number, b: number, c: number): number {
return a + b + c
}
// 定义方式三,约定入参类型及返回值的类型,以及等式左边变量类型
const test3: (a: number, b: number, c: number) => number = function (a: number, b: number, c: number): number {
return a + b + c
}
// 定义方式四,以接口形式定义
interface DemoFunc {
(nameA: string, nameB: string): boolean
}
const test4: DemoFunc = function(nameA: string, nameB: string): boolean {
return nameA === nameB
}
② 入参:
// 参数默认值、可选参数
function test1(a: number, b: number = 2, c?: number): number { // b 指定默认值,c 为可选参数,可选参数必须放在后面
return a + b + (c||0)
}
// 剩余参数
function test2(a, b, ...params) {
// 略,参考 ES6 语法
console.log(params)
}
test2(1,2,3,4,5,6)
③ 重载:
// 重载
function switchType(x: number): string
function switchType(x: string): number
function switchType(x: number | string): number | string {
if (typeof x === 'string') {
return Number(x)
} else if (typeof x === 'number') {
return x + ''
}
}
// 前面是函数定义,最后是函数实现,TypeScript 会优先从最前面的函数定义开始匹配,所以应当先写精确的定义
7 断言类型
关键字 as
① 遇到联合类型的时候,可以将其断言为其中一个类型。举例如下:
interface Triangle { // 接口
sideLength: number
}
interface Circle { // 接口
radius: number
}
function getData(shape: Triangle | Circle) {
// return shape.sideLength // 编译报错
// return shape.radius // 编译报错
return (shape as Triangle).sideLength || (shape as Circle).radius // 断言为子类型
}
const tri: Triangle = {
sideLength: 100
}
console.log(getData(tri))
② 将一个父类断言为更加具体的子类。举例如下:
class Shape {} // 父类
class Triangle extends Shape { // 子类
sideLength: number
}
class Circle extends Shape { // 子类
radius: number
}
function getData(shape: Shape) { // 将父类指定为子类
// return shape.sideLength // 编译报错
// return shape.radius // 编译报错
return (shape as Circle).radius || (shape as Triangle).sideLength // 断言为子类型
}
const tri: Triangle = {
sideLength: 200
}
console.log(getData(tri))
③ 用法小结:
可以将任何类型断言为 any,any 也可以被断言为任何类型;要将 A 断言为 B,只需 A 兼容 B 或 B 兼容 A 即可。
8 声明文件
全局声明写在以 .d.ts 为后缀的类型声明文件里
① 声明全局变量 declare var | declare let | declare const
declare const BASE: string // 仅声明类型,不能定义具体值
② 声明第三方文件
declare let jQuery: (selector: string) => any // 仅定义类型,不能定义具体实现
注意:也可以直接下载第三方文件,直接下载则无需声明为全局变量:npm install @types/jquery --save-dev
③ 声明全局方法 declare function
declare function func(str: string): void // 仅定义方法类型,不能定义具体实现,支持方法重载
④ 声明全局类 declare class
declare class Person {
name: string // 静态变量
constructor(name: string) // 构造函数
getName(): string // 静态方法,不要定义具体实现
}
⑤ 声明全局枚举(外部枚举) declare enum
declare enum Weekdays { mon, tues, wen, thur, fri, sat, sun } // 仅定义类型,不能定义具体值
注意:namespace 关键字属于 TypeScript 早期关键字,为了避免全局变量污染而创建的,随着 ES6 的出现,module 关键字基本上代替了 namespace 的使用。
9 元祖(Tuple)
元祖类似于数组,但存储的数据类型可以是不同的。
const scores:[string, number] = ['Good', 100]
console.log(scores[0])
scores.push('Excellent')
console.log(scores);
scores.pop()
console.log(scores)
10 枚举(Enum)
定义一些带名字的常量使用枚举可以清晰地表达意图或创建一组有区别的用例,TypeScript 支持数字的和基于字符串的枚举。
enum Colors { Yellow, Green, Orange, Blue } // 注意,定义不用等号,值不用写引号
console.log(Colors[0]) // 枚举成员会被赋值为从 0 开始递增的数字
console.log(Colors['Yellow']) // 同时也会对枚举值到枚举名进行反向映射
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat} // 也可以在定义时手动赋值
console.log(Days)
11 类 (Class)
① Typescript 里的类和 Java 类似,可以使用修饰符 public、private 和 protected 修饰类里面定义的属性、构造函数和方法,使用 class 关键字。
public 修饰的属性或方法是公有的,可以在外部访问,默认值;
private 修饰的属性或方法是私有的,不能在外部访问;
protected 修饰的属性或方法和 private 类似,区别在于它在子类中可以被访问。
class Student {
public name: string // 外部可直接访问
protected studentNo: string // 外部不可直接访问,子类可访问
private score: number // 外部和子类均不可直接访问
public constructor(studentNo: string, name: string) {
this.studentNo = studentNo
this.name = name
}
public getName() {
return this.name
}
public setName(name: string) {
return this.name = name
}
public setScore(score: number) {
return this.score = score
}
public getScore() {
return this.score
}
}
const jim = new Student('001','Jim')
jim.setScore(100)
console.log(jim.name) // 可以直接访问 public 修饰的属性
console.log(jim.getName())
// console.log(Jim.studentNo) // 不能访问 protected 修饰的属性,编译报错
// console.log(Jim.score) // 不能访问 private 修饰的属性,编译报错
console.log(jim.getScore()) // 可以通过公有方法访问 private 或 protected 修饰的属性
class StarStudent extends Student { // 创建子类
public constructor(studentNo: string, name: string) {
super(studentNo, name)
console.log(this.studentNo) // 可以访问父类 protected 修饰的属性或方法
// console.log(this.score) // 不能访问父类 private 修饰的属性或方法,编译报错
}
}
const lee = new StarStudent('002', 'Lee')
console.log(lee.getName())
② 定义类时,还可以直接在构造函数中定义属性。例如:
// 以下两种类的定义方式效果一致
class Boy {
public name: string
public constructor(name: string) {
this.name = name
}
}
class Girl {
public constructor(public name:string) {}
}
③ 只读属性 readonly
被 readonly 修饰的属性,只能读,不能改。
class Person {
// public readonly name
public constructor(public readonly name) {
// this.name = name
}
}
let alex = new Person('Alex')
console.log(alex.name) // Alex
// alex.name = 'Alex Zhang' // 编译报错
④ 抽象类 abstract
抽象类不允许被实例化,抽象类中定义的抽象方法必须被子类实现。
abstract class Person {
name: string
public abstract saySomething() // 需要加 abstract 修饰符,否则编译报错
}
// const william = new Person() // 抽象类不能被实例化,编译报错
class Teacher extends Person {
public saySomething () {
console.log('Good Morning, class!') // 抽象方法必须被实现,否则编译报错
}
}
--End(未完待续)--