Young Kbt blog Young Kbt blog
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)

Shp Liu

朝圣的使徒,正在走向编程的至高殿堂!
首页
  • java基础

    • Java基础
    • Java集合
    • Java反射
    • JavaJUC
    • JavaJVM
  • Java容器

    • JavaWeb
  • Java版本新特性

    • Java新特性
  • SQL 数据库

    • MySQL
    • Oracle
  • NoSQL 数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • ActiveMQ
    • RabbitMQ
    • RocketMQ
    • Kafka
  • 进阶服务

    • Nginx
  • Spring
  • Spring Boot
  • Spring Security
  • 设计模式
  • 算法
  • 知识
  • 管理

    • Maven
    • Git
  • 部署

    • Linux
    • Docker
    • Jenkins
    • Kubernetes
  • 进阶

    • TypeScript
  • 框架

    • React
    • Vue2
    • Vue3
  • 轮子工具
  • 项目工程
  • 友情链接
  • 本站

    • 分类
    • 标签
    • 归档
  • 我的

    • 收藏
    • 关于
    • Vue2-Admin (opens new window)
    • Vue3-Admin(完善) (opens new window)
GitHub (opens new window)
  • 超文本标记语言 - Html

  • 解释编程语言 - JavaScript

  • JS 超集语言 - TypeScript

    • TypeScript - 介绍
    • TypeScript - 安装和使用
    • TypeScript - 基本类型
    • TypeScript - 编译和配置
    • TypeScript - 文件打包
    • TypeScript - 接口
    • TypeScript - 函数
    • TypeScript - 类
    • TypeScript - 泛型
    • TypeScript - 类型推断
    • TypeScript - 高级类型
    • TypeScript Office - 入门
    • TypeScript Office - 常用类型
    • TypeScript Office - 类型缩小
    • TypeScript Office - 更多函数
    • TypeScript Office - 对象类型
    • TypeScript Office - 类型操纵
    • TypeScript Office - 类
    • TypeScript Office - 模块
    • TypeScript Office - 变量声明
    • TypeScript Office - 类型推断
    • TypeScript Office - 枚举
    • TypeScript Office - 公共类型
    • TypeScript Office - Symbols
    • TypeScript Office - 类型兼容性
    • TypeScript Office - 迭代器和生成器
    • TypeScript Office - 装饰器
    • TypeScript Office - JSX
    • TypeScript Office - 混入
      • 混入
      • 混入是如何工作的?
      • 受约束的混入
      • 替代模式
      • 限制条件
        • 装饰器和混入
        • 静态属性混入
    • TypeScript Office - 三斜线指令
    • TypeScript Office - 模块进阶
    • TypeScript Office - 模块解析
    • TypeScript Office - 命名空间
    • TypeScript Office - 命名空间与模块
    • TypeScript Office - 声明合并
  • 界面构建框架 - React

  • 渐进式框架 - Vue2

  • 渐进式框架 - Vue3

  • 前端
  • JS 超集语言 - TypeScript
Young Kbt
2022-09-16
目录

TypeScript Office - 混入

  • 混入
  • 混入是如何工作的?
  • 受约束的混入
  • 替代模式
  • 限制条件
    • 装饰器和混入
    • 静态属性混入

# 混入

除了传统的 OO 层次结构外,另一种流行的从可重用组件中建立类的方式是,通过组合更简单的部分类来建立它们。你可能对 Scala 等语言的 mixins 或 traits 的想法很熟悉,这种模式在 JavaScript 社区也达 到了一定的普及。

# 混入是如何工作的?

该模式依赖于使用泛型与类继承来扩展基类。TypeScript 最好的 mixin 支持是通过类表达模式完成的。你可以在 这里 (opens new window) 阅读更多关于这种模式在JavaScript中的工作方式。

为了开始工作,我们需要一个类,在这个类上应用混入:

class Sprite {
    name = "";
    x = 0;
    y = 0;
    constructor(name: string) {
        this.name = name;
    }
}
1
2
3
4
5
6
7
8

然后你需要一个类型和一个工厂函数,它返回一个扩展基类的表达式。

// 为了开始工作,我们需要一个类型,我们将用它来扩展其他类。
// 主要的责任是声明, 传入的类型是一个类。
type Constructor = new (...args: any[]) => {};
// 这个混集器增加了一个 `scale` 属性,并带有getters和setters
// 用来改变它的封装的私有属性。
function Scale<TBase extends Constructor>(Base: TBase) {
    return class Scaling extends Base {
        // 混入不能声明私有/受保护的属性
        // 但是,你可以使用ES2020的私有字段
        _scale = 1;
        setScale(scale: number) {
            this._scale = scale;
        }
        get scale(): number {
            return this._scale;
        }
    };
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

有了这些设置,你就可以创建一个代表基类的类,并应用混合元素。

// 从Sprite类构成一个新的类。
// 用Mixin Scale应用程序:
const EightBitSprite = Scale(Sprite);
const flappySprite = new EightBitSprite("Bird");
flappySprite.setScale(0.8);
console.log(flappySprite.scale);
1
2
3
4
5
6

# 受约束的混入

在上述形式中,混入没有关于类的底层知识,这可能使它很难创建你想要的设计。

为了模拟这一点,我们修改了原来的构造函数类型以接受一个通用参数。

// 这就是我们之前的构造函数
type Constructor = new (...args: any[]) => {};
// 现在我们使用一个通用的版本,它可以在以下方面应用一个约束
// 该混入所适用的类
type GConstructor<T = {}> = new (...args: any[]) => T;
1
2
3
4
5

这允许创建只与受限基类一起工作的类。

type Positionable = GConstructor<{ setPos: (x: number, y: number) => void }>;
type Spritable = GConstructor<Sprite>;
type Loggable = GConstructor<{ print: () => void }>;
1
2
3

然后,你可以创建混入函数,只有当你有一个特定的基础时,它才能发挥作用。

function Jumpable<TBase extends Positionable>(Base: TBase) {
    return class Jumpable extends Base {
        jump() {
            // 这个混合器只有在传递给基类的情况下才会起作用。
            // 类中定义了setPos,因为有了可定位的约束。
            this.setPos(0, 20);
        }
    };
}
1
2
3
4
5
6
7
8
9

# 替代模式

本文档的前几个版本推荐了一种编写混入函数的方法,即分别创建运行时和类型层次,然后在最后将它们合并:

// 每个mixin都是一个传统的ES类
class Jumpable {
    jump() {}
}
class Duckable {
    duck() {}
}
// 基类
class Sprite {
    x = 0;
    y = 0;
}
// 然后,你创建一个接口,
// 将预期的混合函数与你的基础函数同名,
// 合并在一起。
interface Sprite extends Jumpable, Duckable {}
// 在运行时,通过JS将混入应用到基类中
applyMixins(Sprite, [Jumpable, Duckable]);
let player = new Sprite();
player.jump();
console.log(player.x, player.y);

// 它可以存在于你代码库的任何地方
function applyMixins(derivedCtor: any, constructors: any[]) {
    constructors.forEach((baseCtor) => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach((name) => {
            Object.defineProperty(
                derivedCtor.prototype,
                name,
                Object.getOwnPropertyDescriptor(baseCtor.prototype, name) ||
                Object.create(null)
            );
        });
    });
}
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

这种模式较少依赖于编译器,而更多地依赖于你的代码库,以确保运行时和类型系统都能正确地保持同步。

# 限制条件

mixin 模式在 TypeScript 编译器中通过代码流分析得到了本地支持。在一些情况下,你会遇到本地支持的边界。

# 装饰器和混入

你不能使用装饰器来通过代码流分析提供混入:

// 一个复制mixin模式的装饰器函数。
const Pausable = (target: typeof Player) => {
    return class Pausable extends target {
        shouldFreeze = false;
    };
};
@Pausable
class Player {
    x = 0;
    y = 0;
}
// 播放器类没有合并装饰器的类型
const player = new Player();
player.shouldFreeze;
// Ⓧ 属性'shouldFreeze'在类型'Player'上不存在
// 运行时方面可以通过类型组合或接口合并来手动复制。
type FreezablePlayer = Player & { shouldFreeze: boolean };
const playerTwo = (new Player() as unknown) as FreezablePlayer;
playerTwo.shouldFreeze;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 静态属性混入

与其说是约束,不如说是一个难题。类表达式模式创建了单子,所以它们不能在类型系统中被映射以支持不同的变量类型。

你可以通过使用函数返回你的类来解决这个问题,这些类基于泛型而不同:

function base<T>() {
    class Base {
        static prop: T;
    }
    return Base;
}
function derived<T>() {
    class Derived extends base<T>() {
        static anotherProp: T;
    }
    return Derived;
}
class Spec extends derived<string>() {}
Spec.prop; // string
Spec.anotherProp; // string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
编辑此页 (opens new window)
#TypeScript
更新时间: 2023/09/18, 16:34:13
TypeScript Office - JSX
TypeScript Office - 三斜线指令

← TypeScript Office - JSX TypeScript Office - 三斜线指令→

最近更新
01
技术随笔 - Element Plus 修改包名 原创
11-02
02
Reactor - 扩展性
11-02
03
Reactor - 最佳实践
11-02
更多文章>
Theme by Vdoing | Copyright © 2021-2024 Young Kbt | blog
桂ICP备2021009994号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式