TypeScript的类型编程功能非常强大
甚至有人用类型编程做了个中国象棋:zhuanlan.zhihu.com/p/426966480
注意观察这个象棋程序里有多少个嵌套的
extends
但是,正是类型编程的强大,导致它带来的复杂度无法承受。
故事
有一个具体的场景,导致我在一瞬间丧失了对类型编程的兴趣。
需求背景
当时项目里需要建立一个链接,接受消息通知。
我决定在网页加载时就建立一个链接,并分发消息。
其他地方只需要监听消息。
代码设计
有几种不同的消息
enum EventsType {
begin,
end,
pause,
}
每种消息的有不同的数据结构
type Begin = {...}
type End = {...}
type Pause = {...}
然后,
- 需要给每种消息,都生命一个数组,存放回调函数。
- 需要给每种消息,都提供一个对外暴露的,注册回调的函数。
显然,这两部操作除了消息类型不一样,逻辑都是一样的。 于是我写了一个高阶函数,在闭包里保存回调函数数组,并返回注册用的函数。
const listenerFactory =
<R extends MessageType>(eventType: R['type']) =>
(cb: (message: R) => void) => {
const _cb = (message: MessageType) => {
if (message.type !== eventType) return;
cb(message as R);
};
callbacks.push(_cb);
const remove = () => {
pull(callbacks, _cb);
};
/**
* remove the listener
*/
return remove;
};
用法
export const onBegin = listenerFactory(EventsType.Begin);
export const onEnd = listenerFactory(EventsType.End);
export const onPause = listenerFactory(EventsType.Pause);
类型编程
但是现在有一个问题是高阶函数listenerFactory无法自动推断出R的类型。
这时,我可能需要搞一点类型编程了,弄一个类型函数,让回调函数能自动获得类型约束。
我大概需要写一堆嵌套的extends判断来递归的找到R的正确的类型。
我很兴奋,学到的类型编程技巧,有用武之地了。
结果我在三秒之后就丧失了兴趣!!!
我为什么要花时间弄这个类型编程?仅仅是为了VSCode能弹出一个菜单让我选?
而且整个项目是只有我一个人在开发。
我为什么不直接把类型写死,以避免类型编程呢?
就像这样:
export const onBegin = listenerFactory<Begin>(EventsType.Begin);
探讨
React
想象给把React改成TypeScript。
FiberNode里有大量相同属性名,但值类型不同的数据结构
万一TypeScript做不到自动推断,得写多少类型编程的代码?
Vue
Vue2的选项是api,因为属性,方法都是写在对象上,导致类型编程无法避免。
而且是事后添加对TypeScript的支持,类型编程更麻烦
作者在一个播客上聊过这件事
TypeScript编译器甚至专门提供了一个
ThisType解决这个问题
Dart
类型编程这个东西,在大部分语言里都没用,但是这些语言仍然提供了良好的类型系统。
比如Dart也是强类型,没有类型编程带来的复杂度,但是仍然有强类型的好处。
结论
类型编程在大部分情况下都是不必要的。