1.class(类)
1.1类(属性)
class (类)的属性和方法,且以大写字母开头
class Person {
name: string
gender: string
age: number = 18
constructor(name: string,username:string, gender:string) {
this.name = name
this.username = username
this.gender =gender
}
printAge(age: number) {
this.age = age
console.log(this.age)
}
}
- 创建一个 person类型的新对象,并执行构造函数初始化它
const person = new Person('viole', '王一博', '女')
console.log(person.name, person.username) // viole 王一博
1.1.2 公共,私有与受保护的修饰符
默认为 public
- 公共(public)
在TypeScript里,成员都默认为 public。
class Person {
public name: string
public gender: string
public age: number = 18
}
- 私有(private)
当成员被标记成 private时,它就不能在声明它的类的外部访问。比如:
class Friute {
private name: string;
constructor(fName: string) { this.name = fName; }
}
new Friute("Cat").name; // 错误: 'name' 是私有的.
- 但是可以在类内部暴露方法,供类对象调用
class Person {
public name: string
protected gender: string
private age: number = 18
constructor(name: string, public username: string, gender: string) {
this.name = name
this.username = username
this.gender = gender // 被保护的,是不能在外面访问的
}
printAge(age: number) {
this.age = age
console.log(this.age)
}
}
person.printAge(16) // 16
- 受保护的(protected)
protected修饰符与 private修饰符的行为很相似,但有一点不同, protected成员在派生类中仍然可以访问。例如:
class Person {
public name: string
protected gender: string
private age: number = 18
constructor(name: string, public username: string, gender: string) {
this.name = name
this.username = username
this.gender = gender // 被保护的,是不能在外面访问的
}
printAge(age: number) {
this.age = age
console.log(this.age)
}
setGender(gender: string) {
this.gender = gender
console.log(this.gender) // 女
}
printName(name: string) {
this.name = name
console.log(this.name)
}
}
const person = new Person('viole', '王一博', '女')
console.log(person.age) // error age is private
console.log(person.gender) // error gender is protected
// 但是可以通过方法赋值
person.setGender('女')
构造函数也可以被标记成 protected。 这意味着这个类不能在包含它的类外被实例化,但是能被继承。比如:
class Person1 {
protected name: string
protected constructor(theName: string) {
this.name = theName
}
}
class Teacher extends Person1 {
private teacherId: number
constructor(name: string, teacherId: number) {
super(name)
this.teacherId = teacherId
}
public getElevInfo() {
return `my age is ${this.name}and my id is ${this.teacherId}`
}
}
let val = new Teacher('violet', 1234567)
console.log(val.getElevInfo()) // my age is violetand my id is 1234567
let john = new Person1('John') // error: 'Person1' 的构造函数是被保护的.
- readonly(只读)修饰符
你可以使用 readonly关键字将属性设置为只读的。 只读属性必须在声明时或构造函数里被初始化。
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 7;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 7 strong legs");
dad.name = "Man with the 3-piece suit"; // 错误! name 是只读的.
1.2类的继承
- 创建子类 Student 类继承于Person
子类可以继承父类的属性,但是不能使用私有属性
class Student extends Person {
studentId: number
constructor(name: string, username: string, studentId: number) {
super(name, username)
this.studentId = studentId
console.log(this.gender) // 被保护的可以访问 undefined
// console.log(this.age) // error 私有属性不能在子类中使用
}
studentPrint() {
console.log(this.studentId)
}
// 重写父类方法
printName(name: string) {
this.name = name
console.log(this.name)
}
}
const minswu = new Student('violet3', '春日宴', 127051)
console.log(minswu)
console.log(minswu.studentPrint()) // 127051
minswu.printAge(16) // 16
console.log(minswu.name, minswu.username, minswu.studentId) // violet3 春日宴 127051
类从基类中继承了属性和方法。 这里, minswu是一个 派生类,它派生自 Student 基类,通过 extends关键字。 派生类通常被称作 子类,基类通常被称作 超类。
1.3 类的get和set
存取器:
TypeScript支持通过getters/setters来截取对对象成员的访问
- 用于隔离私有属性和 可公开属性
class Car {
private _color: string = 'red'
// 私有属性赋值
set setColor(val: string) {
this._color = val
}
// 私有属性取值
get getColer() {
return this._color
}
}
let byeCar = new Car()
console.log(byeCar.getColer) // red
byeCar.setColor = 'blue'
console.log(byeCar.getColer) // blue
- 静态属性(static)
class Car {
private _color: string = 'red'
// 定义一个静态常量
// PI: number = 3.14
// 定义一个静态属性
static PI: number = 3.14
// 私有属性赋值
// 定义静态方法,计算圆
static calcCircle(value: number): number {
return this.PI * value
}
set setColor(val: string) {
this._color = val
}
// 私有属性取值
get getColer() {
return this._color
}
}
- 通过类实例对象进行访问PI常量
let byeCar = new Car()
// 定义一个静态常量
PI: number = 3.14
console.log(byeCar.PI)
console.log(byeCar.calcCircle(8)) // 25.12
- 2.通过static访问,及通过类直接访问静态属性,静态方法
console.log(Car.PI) // 3.14
console.log(Car.calcCircle(8)) // 25.12
我们也可以创建类的静态成员,这些属性存在于类本身上面而不是类的实例上
2.命名空间(nameSpace)
2.1初识命名空间
“内部模块”现在称做“命名空间”。 “外部模块”现在则简称为“模块”,
优点:解决变量污染,分离文件,便于维护
- 使用namespace关键字开辟一个空间
- 里面的属性,函数需要使用export 关键字导出才可以被外部访问到
namespace MyMath {
export const PI = 3.14
export function sumValue(num1: number, num2: number): number {
return num1 + num2
}
export function calcCircle(val: number) {
return val * PI
}
}
const PI = 3.1415926
// 外面需要使用调用方法,里面需要export
console.log(MyMath.sumValue(5, 10)) // 15
console.log(PI) // 3.1415926
console.log(MyMath.PI) // 3.14
2.2命名空间文件拆分
当应用变得越来越大时,我们需要将代码分离到不同的文件中以便于维护。
- 将namespace.ts 文件中将sumvalue 和calcCircle 分成独立的文件
// circle.ts
namespace MyMath {
export const PI = 3.14
export function calcCircle(val: number) {
return val * PI
}
}
//sumvalue.ts
namespace MyMath {
export function sumValue(num1: number, num2: number): number {
return num1 + num2
}
}
- 在namespace.ts中访问MyMath空间方法
// namespace.ts
console.log(MyMath.sumValue(5, 10)) // error 找不到MyMath
console.log(MyMath.calcCircle(8)) // error 找不到MyMath
方法一:
在index.html中引入拆分出去的sumValue.js,circle.js文件
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body></body>
<script src="./two/app.js"></script>
<script src="./two/circle.js"></script>
<script src="./two/sumValue.js"></script>
<script src="./two/namespace.js"></script>
</html>
- 此时能访问
// namespace.ts
// 方法一:将 将sumvalue.js 和和calcCircle.js 单独引入到index.html
console.log(MyMath.sumValue(5, 10)) // 15
console.log(MyMath.calcCircle(8)) // 25.12
方法二:
将三个将sumvalue.circle.ts 以及 namespace.ts合成一个js文件,例如:
- 使用--outfile 关键字在终端执行命令,将三个文件合并成app.js
tsc --outfile app.js circle.ts sumvalue.ts namespace.ts
- 在index.html中引用app.js
// app.js
var MyMath;
(function (MyMath) {
MyMath.PI = 3.14;
function calcCircle(val) {
return val * MyMath.PI;
}
MyMath.calcCircle = calcCircle;
})(MyMath || (MyMath = {}));
// 将文件单独出来
var MyMath;
(function (MyMath) {
function sumValue(num1, num2) {
return num1 + num2;
}
MyMath.sumValue = sumValue;
})(MyMath || (MyMath = {}));
console.log(MyMath.sumValue(5, 10)) // 15
console.log(MyMath.calcCircle(8)) // 25.12
2.3多重命名空间及引入文件
- 第一步,将vscode ts自动转换成js 插件关闭掉
- 新建circle.ts
// circle.ts
// 给MyMath里面的circle设置了namespace Circle,外部文件能访问一定要export
namespace MyMath {
export namespace Circle {
export const PI = 3.14
export function calcCircle(val: number) {
return val * PI
}
}
}
- 新建sumValue.ts
namespace MyMath {
export function sumValue(num1: number, num2: number): number {
return num1 + num2
}
}
- 新建app.ts
// app.ts
console.log(MyMath.sumValue(5, 20)) // error
console.log(MyMath.Circle.calcCircle(8)) error 25.12
- 方法一:将代码合成为app.js(同上)
- 方法二,在app.ts文件里面引入circle.ts文件和sumValue.ts文件
语法: /// <reference path="文见名.ts" />
// app.ts
/// <reference path="circle.ts" />
/// <reference path="sumValue.ts" />
// 第一步,将vscode ts自动转换成js 插件关闭掉
console.log(MyMath.sumValue(5, 20)) // 25
console.log(MyMath.Circle.calcCircle(8)) // 25.12
- 然后在终端执行命令:
tsc app.ts --outFile app.js
3.模块module的使用
SystemJS 是一个通用的模块加载器,它能在浏览器或者 NodeJS 上动态加载模块,并且支持 CommonJS、AMD、全局模块对象和 ES6 模块。通过使用插件,它不仅可以加载 JavaScript,还可以加载 CoffeeScript 和 TypeScript。
4.interface(接口)
4.1 接口初识
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。
interface Flowers {
name: string
time: number
greet(): void
}
// interface 接口默认都是必填字段
let ginkgo: Flowers = {
name: '牡丹',
time: 3,
greet() {
console.log('洛阳牡丹')
},
}
- 有时,往往会拿type类型与interface比较
type Flowers2 = { name: String; time: number }
interface 可以继承 type 不能继承,在类(calss) 中一般使用interface
可选属性
接口里的属性不全都是必需的。
// 参数名?: 可选的
interface Flowers {
name: string
time: number
color?: string
greet(): void
}
只读属性
- 关键字:readonly,只能在初始化赋值不能修改
interface Flowers {
name: string
time: number
color?: string
readonly city: string // 只读不能修改
greet(): void
}
let ginkgo: Flowers = {
name: '牡丹',
time: 3,
city: '洛阳',
greet() {
console.log('洛阳牡丹')
},
}
// ginkgo.ginkgo = '北京' // error 只读不能修改
额外的属性检查
'[变量:变量类型]':数值类型
interface Flowers {
name: string
time: number
color?: string
readonly city: string // 只读不能修改
[paramsName: string]: any // 任何类型的任意参数名字
greet(): void
}
// ids 就是任意名字的参数
let ginkgo: Flowers = {
name: '牡丹',
time: 3,
city: '洛阳',
ids: [1.3, 4],
greet() {
console.log('洛阳牡丹')
},
}
function printFlowers(ginkgo) {
console.log(`唯有${ginkgo.name}真国色,花开时节动京城`)
}
printFlowers(ginkgo) //唯有牡丹真国色,花开时节动京城
4.2 接口继承及类的实现
4.2.1接口在class 中的应用
- 定义一个interface(接口)
// 接口花
interface Flowers {
name: string
time: number
color?: string
readonly city: string // 只读不能修改
[paramsName: string]: any // 任何类型的任意参数名字
greet(): void
}
- 正常创建类
创建一个类
class Plum {
name: string
age: number
time: number
color: string
}
- implements:通过interface实现一个类
implements:实现 Plum要根据Flowers的属性或者方法来定义(就是属性与方法保持一致,必填项类型)
// 根据Flowers 实现梅花类
class Plum implements Flowers {
name: string = '墨梅'
city: string = '门前'
time: number = 3
color: string = 'white'
desc: string = '有大有小 枝上同宿'
greet() {
console.log('洗妆真态,不作铅华御')
}
- 根据interface 实现一个类
// 定义春天的花接口
interface SpringFlowers {
id: number
moth: number
}
// 根据接口 Flowers,接口SpringFlowers实现类
class Plum implements Flowers, SpringFlowers {
id: number = 101
moth: number = 12
name: string = '墨梅'
city: string = '门前'
time: number = 3
color: string = 'white'
desc: string = '有大有小 枝上同宿'
greet() {
console.log('洗妆真态,不作铅华御')
}
}
4.2.2 interface 接口的继承
- 关键字extends
// 桃花继承至花接口
interface Peach extends Flowers {}
const peach: Peach = {
name: '桃花',
city: '门前',
time: 4,
color: 'pink',
greet() {
console.log('人面桃花相映红')
},
}
console.log(peach, 'peach==')
/* r: "pink", greet: ƒ}
city: "门前"
color: "pink"
greet: ƒ ()
name: "桃花"
time: 4 */
5.Generic(泛型)
5.1 在函数中的使用泛型
- 不要泛型的函数可能是这样
function createSpring(arg: string):string {
return arg
}
console.log(createSpring('小草')) // 小草
- 或者使用any
// 或者在实参不确定类型的情况下,可以使用any
function createSpring(arg: any):any {
return arg
}
但是使用any类型会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。所以,在ts中一般不建议使用any
- 泛型:返回值的类型与传入参数的类型是相同的,
会根据调用的实参推断参数类型
符合:类型变量T
function createSpring<T>(arg: T): T {
return arg
}
// 可以在调用的时候,明确指定类型
console.log(createSpring<string>('小草')) //小草
// 交给ts 推断类型
console.log(createSpring(true)) // true
给createSpring添加类型变量T。 T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T当做返回值类型
- 在接口中使用泛型,定义一个变量 是泛型
interface GenFlowers {
<T>(arg: T): T
}
function createdPlum<T>(arg: T): T {
return arg
}
// 定义一个变量
let plum: GenFlowers = createdPlum
// 交给ts 推断类型
console.log(plum(30)) //30
// 可以明确指定类型
console.log(plum<string>('墨梅')) //墨梅
- 直接在接口层面上 定义泛型
interface GenSpringFlowers<T> {
(arg: T): T
}
// 在定义调用的时候,需要指定类型,string 或者number
let peachs: GenSpringFlowers<string | number> = createdPlum
console.log(peachs('桃花')) //桃花
- 为泛型添加约束
// 此时我们需要添加约束,参数里面必须要拥有length这个属性
function getLength<T extends { length: any }>(obj: T): any {
return obj
}
const obj = {
name: 'violet',
age: 30,
length: 10,
}
console.log(getLength(obj)) // {age: 30,name: "violet",length: 10,}
- 或者指定类型 为number,那么传入的就必须是number,就不能是对象
function getLength<T extends number>(obj: T): any {
return obj
}
const obj = 25
console.log(getLength(obj)) // 25
5.2 泛型Generic类的应用
- 定义一个class
class CountNumber {
num1: number
num2: number
constructor(num1: number, num2: number) {
this.num1 = num1
this.num2 = num2
}
whereabouts(): number {
return this.num1 * this.num2
}
}
const p1 = new CountNumber<number>(10, 40)
console.log(p1.whereabouts()) // 400
- 给class加上泛型
class CountNumber<T> {
num1: T
num2: T
constructor(num1: T, num2: T) {
this.num1 = num1
this.num2 = num2
}
whereabouts(): number {
// 由于是使用的类型T 进行计算时 ts需要在前缀加上+
return +this.num1 * +this.num2
}
}
const p1 = new CountNumber<number>(10, 40)
console.log(p1.whereabouts()) // 400
const p2 = new CountNumber<string>('10', '40')
console.log(p2.whereabouts()) // 400
- 给泛型进行约束,那么就必须传number
class CountNumber<T extends number> {
num1: T
num2: T
constructor(num1: T, num2: T) {
this.num1 = num1
this.num2 = num2
}
whereabouts(): number {
// 由于是使用的类型T 进行计算时 ts需要在前缀加上+
return +this.num1 * +this.num2
}
}
// 此时传入的实参就必须是number
// const p1 = new CountNumber('10', '40') // error
const p1 = new CountNumber(10, 40)
console.log(p1.whereabouts()) // 400