再学TS基础 - 图1

数据类型

再学TS基础 - 图2

函数

再学TS基础 - 图3

"strictPropertyInitialization": true 启用类属性初始化的严格检查

  1. /**
  2. * 当我们写一个类的时候,会得到2个类型
  3. * 1. 构造函数类型的函数类型
  4. * 2. 类的实例类型
  5. */
  6. class Component {
  7. static myName: string = '静态名称属性'
  8. myName: string = '实例名称属性'
  9. }
  10. let com = Component
  11. //Component类名本身表示的是实例的类型
  12. //ts 一个类型 一个叫值
  13. //冒号后面的是类型
  14. //放在=后面的是值
  15. let c: Component = new Component()
  16. let f: typeof Component = com

readonly

  • readonly修饰的变量只能在构造函数中初始化
  • 允许将 interface、type、 class 上的属性标识为 readonly
  • readonly 实际上只是在编译阶段进行代码检查。而 const 则会在运行时检查(在支持 const 语法的 JavaScript 运行时环境中) ```typescript class Animal { public readonly name: string constructor(name: string) { this.name = name } changeName(name: string) { this.name = name // error } }

let a = new Animal(‘lc’) a.changeName(‘cl’)

  1. <a name="VXSWF"></a>
  2. ### 修饰符
  3. ```typescript
  4. class Father {
  5. public name: string //类里面 子类 其它任何地方外边都可以访问
  6. protected age: number //类里面 子类 都可以访问,其它任何地方不能访问
  7. private money: number //类里面可以访问, 子类和其它任何地方都不可以访问
  8. constructor(name: string, age: number, money: number) {
  9. //构造函数
  10. this.name = name
  11. this.age = age
  12. this.money = money
  13. }
  14. getName(): string {
  15. return this.name
  16. }
  17. setName(name: string): void {
  18. this.name = name
  19. }
  20. }
  21. class Child extends Father {
  22. constructor(name: string, age: number, money: number) {
  23. super(name, age, money)
  24. }
  25. desc() {
  26. console.log(`${this.name} ${this.age} ${this.money}`)
  27. }
  28. }
  29. let child = new Child('lc', 10, 1000)
  30. console.log(child.name)
  31. console.log(child.age) // error
  32. console.log(child.money) // error

抽象类

  • 抽象描述一种抽象的概念,无法被实例化,只能被继承
  • 无法创建抽象类的实例
  • 抽象方法不能在抽象类中实现,只能在抽象类的具体子类中实现,而且必须实现

    1. abstract class Animal {
    2. name!: string
    3. abstract speak(): void
    4. }
    5. class Cat extends Animal {
    6. speak() {
    7. console.log('喵喵喵')
    8. }
    9. }
    10. let animal = new Animal() // 无法创建抽象类的实例
    11. animal.speak()
    12. let cat = new Cat()
    13. cat.speak()

    抽象方法

  • 抽象类和方法不包含具体实现,必须在子类中实现

  • 抽象方法只能出现在抽象类中
  • 子类可以对抽象类进行不同的实现

    1. abstract class Animal {
    2. abstract speak(): void
    3. }
    4. class Dog extends Animal {
    5. speak() {
    6. console.log('汪汪汪')
    7. }
    8. }
    9. class Cat extends Animal {
    10. speak() {
    11. console.log('喵喵喵')
    12. }
    13. }
    14. let dog = new Dog()
    15. let cat = new Cat()
    16. dog.speak()
    17. cat.speak()

    重写(override) vs 重载(overload)

  • 重写是指子类重写继承自父类中的方法

  • 重载是指为同一个函数提供多个类型定义

继承 vs 多态

  • 继承(Inheritance)子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性
  • 多态(Polymorphism)由继承而产生了相关的不同的类,对同一个方法可以有不同的行为

    装饰器

  • 装饰器是一种特殊类型的声明,它能够被附加到类声明、方法、属性或参数上,可以修改类的行为

  • 常见的装饰器有类装饰器、属性装饰器、方法装饰器和参数装饰器
  • 装饰器的写法分为普通装饰器和装饰器工厂

装饰器使用 @expression 的形式,其中 expression 必须能够演算为在运行时调用的函数,其中包括装饰声明信息。在不改变对象自身的基础上,动态增加额外的职责。把对象核心职责和要装饰的功能分开了。非侵入式的行为修改。
image.png
Typescript 中的装饰器
expression 求值后为一个函数,它在运行时被调用,被装饰的声明信息会被做为参数传入。

  1. class Person {
  2. @time
  3. say() {
  4. console.log('hello')
  5. }
  6. }

Javascript规范里的装饰器目前处在 建议征集的第二阶段,https://github.com/tc39/proposal-decorators 也就意味着不能在原生代码中直接使用,浏览器暂不支持。
TypeScript 工具在编译阶段,把装饰器语法转换成浏览器可执行的代码。

  1. // tsconfig 中开启
  2. "experimentalDecorators": true

装饰器分类

类装饰器

  • 类装饰器在类声明之前声明,用来监视、修改或替换类定义

    属性装饰器

  • 属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数

  • 属性装饰器用来装饰属性
    • 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    • 第二个参数是属性的名称
  • 方法装饰器用来装饰方法

    • 第一个参数对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
    • 第二个参数是方法的名称
    • 第三个参数是方法描述符

      参数装饰器

  • 会在运行时当作函数被调用,可以使用参数装饰器为类的原型增加一些元数据

    • 第1个参数对于静态成员是类的构造函数,对于实例成员是类的原型对象
    • 第2个参数的名称
    • 第3个参数在函数列表中的索引

装饰器执行顺序

  • 有多个参数装饰器时(复合装饰):从最后一个参数依次向前执行
  • 方法和方法参数中参数装饰器先执行。
  • 类装饰器总是最后执行
  • 方法和属性装饰器,谁在前面谁先执行。因为参数属于方法一部分,所以参数会一直紧紧挨着方法执行
  • 类比React组件的componentDidMount 先上后下、先内后外

    装饰器执行演示

    ```typescript function d1(target: Function) { console.log(‘———————-d1类装饰器——————————-‘) console.log(target) console.log(typeof target) }

function d2(target: any, name: string) { console.log(‘———————-d2属性装饰器——————————-‘) console.log(typeof target, name) }

function d3(target: any, name: string, descriptor: PropertyDescriptor) { console.log(‘———————-d3访问器装饰器——————————-‘) console.log(typeof target, name) console.log(descriptor) } function d4(target: any, name: string, descriptor: PropertyDescriptor) { console.log(‘———————-d4方法装饰器——————————-‘) console.log(typeof target, name) console.log(descriptor) } function d5(target: any, name: string, index: number) { // name 是当前参数所在的方法 console.log(‘———————-d5参数装饰器——————————-‘) console.log(typeof target, name) console.log(index) }

@d1 class MyClass { @d2 static property1: number

@d2 a: number

@d3 get b() { return 1 } @d3 static get c() { return 2 }

@d4 public method1(@d5 x: number, @d5 y: number) {} @d4 public static method2() {} }

  1. ```shell
  2. ---------------d2属性装饰器---------------------
  3. object a
  4. ---------------d3访问器装饰器---------------------
  5. object b
  6. {
  7. get: [Function: get],
  8. set: undefined,
  9. enumerable: false,
  10. configurable: true
  11. }
  12. ---------------d5参数装饰器---------------------
  13. object method1
  14. 1
  15. ---------------d5参数装饰器---------------------
  16. object method1
  17. 0
  18. ---------------d4方法装饰器---------------------
  19. object method1
  20. {
  21. value: [Function],
  22. writable: true,
  23. enumerable: true,
  24. configurable: true
  25. }
  26. ---------------d2属性装饰器---------------------
  27. function property1
  28. ---------------d3访问器装饰器---------------------
  29. function c
  30. {
  31. get: [Function: get],
  32. set: undefined,
  33. enumerable: false,
  34. configurable: true
  35. }
  36. ---------------d4方法装饰器---------------------
  37. function method2
  38. {
  39. value: [Function],
  40. writable: true,
  41. enumerable: true,
  42. configurable: true
  43. }
  44. ---------------d1类装饰器---------------------
  45. [Function: MyClass] { method2: [Function] }
  46. function

TS装饰器原理

  • 装饰器本质就是一个函数
  • 利用被装饰目标的prototype来对其进行扩展

    1. function Path(baseUrl: string) {
    2. return function (target) {
    3. target.prototype.$Meta = {
    4. baseUrl: baseUrl
    5. }
    6. }
    7. }

    接口interface

  • interface中可以用分号或者逗号分割每一项,也可以什么都不加

对象接口

  1. //接口可以用来描述`对象的形状`,少属性或者多属性都会报错
  2. interface Speakable {
  3. speak(): void
  4. name?: string //?表示可选属性
  5. }
  6. let speakman: Speakable = {
  7. speak() {}, //少属性会报错
  8. name,
  9. age, //多属性也会报错
  10. }

行为抽象

  1. //接口可以在面向对象编程中表示为行为的抽象
  2. interface Speakable {
  3. speak(): void
  4. }
  5. interface Eatable {
  6. eat(): void
  7. }
  8. //一个类可以实现多个接口
  9. class Person implements Speakable, Eatable {
  10. speak() {
  11. console.log('Person说话')
  12. }
  13. eat() {}
  14. }
  15. class TangDuck implements Speakable {
  16. speak() {
  17. console.log('TangDuck说话')
  18. }
  19. eat() {}
  20. }

任意属性

  1. //无法预先知道有哪些新的属性的时候,可以使用 `[propName:string]:any`,propName名字是任意的
  2. interface Person {
  3. readonly id: number
  4. name: string
  5. [propName: string]: any
  6. }
  7. let p1 = {
  8. id: 1,
  9. name: 'lc',
  10. age: 10,
  11. }

接口继承

  1. interface Speakable {
  2. speak(): void
  3. }
  4. interface SpeakChinese extends Speakable {
  5. speakChinese(): void
  6. }
  7. class Person implements SpeakChinese {
  8. speak() {
  9. console.log('Person')
  10. }
  11. speakChinese() {
  12. console.log('speakChinese')
  13. }
  14. }

可索引接口

  1. interface UserInterface {
  2. [index: number]: string
  3. }
  4. let arr: UserInterface = ['value1', 'value2']
  5. console.log(arr)
  6. interface UserInterface2 {
  7. [index: string]: string
  8. }
  9. let obj: UserInterface2 = { name: 'value' }

类接口

  1. interface Speakable {
  2. name: string
  3. speak(words: string): void
  4. }
  5. class Dog implements Speakable {
  6. name!: string
  7. speak(words: string) {
  8. console.log(words)
  9. }
  10. }
  11. let dog = new Dog()
  12. dog.speak('汪汪汪')

interface vs type

相同点

  • 描述一个对象或者函数
  • 二者都可以被继承
  • interface和type都能够被扩展,

不同点

  • type可以定义基本类型别名、联合类型、元组,但是interface无法定义
  • interface声明可以合并(多次声明),type不行
  • interface可以拓展type,但是type不能继承interface,type可以使用&联合类型来实现类似的功能

扩展(extends)与交叉类型(intersection types)

  • interface 可以 extends,type 不允许 extends和implement的,type可以通过交叉类型实现 interface 的extends行为。
  • 并且两者并不是相互独立的,也就是说 interface 可以 extends type , type也可以与 interface类型交叉。

推荐使用interface无法实现的功能再用type

命名空间

  • 在代码量较大的情况下,为了避免命名空间冲突,可以将相似的函数、类、接口放置到命名空间内
  • 命名空间可以将代码包裹起来,只对外暴露需要在外部访问的对象,命名空间内通过export向外导出
  • 命名空间是内部模块,主要用于组织代码,避免命名冲突
  1. export namespace zoo {
  2. export class Dog {
  3. eat() {
  4. console.log('zoo dog')
  5. }
  6. }
  7. }
  8. export namespace home {
  9. export class Dog {
  10. eat() {
  11. console.log('home dog')
  12. }
  13. }
  14. }
  15. let dog_of_zoo = new zoo.Dog()
  16. dog_of_zoo.eat()
  17. let dog_of_home = new home.Dog()
  18. dog_of_home.eat()

原理

  • 其实一个命名空间本质上一个对象,它的作用是将一系列相关的全局变量组织到一个对象的属性
  1. namespace Numbers {
  2. export let a = 1
  3. export let b = 2
  4. export let c = 3
  5. }
  6. var Numbers
  7. ;(function (Numbers) {
  8. Numbers.a = 1
  9. Numbers.b = 2
  10. Numbers.c = 3
  11. })(Numbers || (Numbers = {}))

可合并

  1. namespace k1 {
  2. let a = 10
  3. export var obj = {
  4. a,
  5. }
  6. }
  7. namespace k1 {
  8. let b = 20
  9. export var obj2 = {
  10. b,
  11. }
  12. }
  13. namespace k2 {
  14. console.log(k1)
  15. }

类型声明

  • 声明文件可以让我们不需要将JS重构为TS,只需要加上声明文件就可以使用系统
  • 类型声明在编译的时候都会被删除,不会影响真正的代码
  • 关键字 declare 表示声明的意思,我们可以用它来做出各种声明:
  1. declare let b: {
  2. v: number
  3. }
  4. export default g

TypeScript 模块解析策略

TypeScript 现在使用了与 Node.js 类似的模块解析策略,但是 TypeScript 增加了其它几个源文件扩展名的查找(.ts、.tsx、.d.ts),同时 TypeScript 在 package.json 里使用字段 types来表示查找路径