今天早上在做类型体操的“first”这一题。 我学习到了 “infer” 关键词的用法。
type-challenges/README.zh-CN.md at main · type-challenges/type-challenges · GitHub (kgithub.com)
对我来说,这是在接触一个新概念,我不禁开始思考一些逻辑学的事情:
- 我如何基于已有的逻辑,推理出新的逻辑?
- 程序语言中的“关键词”到底应该怎么去理解/使用/记忆?
type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];
type head1 = First<arr1>; // expected to be 'a'
type head2 = First<arr2>; // expected to be 3
type First<Arr> = Arr extends { 0: infer Index0Type } ? Index0Type : never;
适宜人脑直接思考的逻辑思维:语义化表述。
“想出新逻辑”的过程就是:将多个 “语义片段” 随意组合起来。或者对已有逻辑中的语义片段做 “同类替换”。
关注程序语言中 extends,infer,?,特殊结构(比如{0: infer xxx}) 所代表的“语义”
所谓的“语义”就是一大串自然语言的解释。
就像逻辑学 / 数学 习惯于将一大串“语义”然后赋予到一个简洁的特殊符号 “->” 上一样。
我只需要真正学会赋予给 “extends,infer,?” 这些符号的语义,我就能完成编程。
一般来说,提出这些符号的 “RFC提案” 中就蕴含了这些符号的设计者们,赋予给这个符号的语义。
然后下一步
将复杂语义寄宿在简单符号中,我们仅仅使用简洁语句,就能进行包含丰富语义的思考。
降低了复杂逻辑所需要的思维量,变相提升逻辑思维能力。
我尝试用这种思维方式,做下一题
type-challenges/README.zh-CN.md at main · type-challenges/type-challenges · GitHub (kgithub.com)
type ExampleType = Promise<string>;
type Result = MyAwaited<ExampleType>; // string
type MyAwaited<T> = T extends Promise<infer P> ? P : never;
基于我今天提出的 “理解关键字/符号背后寄宿的 ‘语义片段’” 这一方法
我在做这题的时候
简单地把题目给出的语义先理解了一次。
然后把我脑子里存储的 extends的语义,infer的语义,type XXX = {} 这个语法结构的语义,都遍历了一次。
我先做好接口设计。MyAwaited (“接口”就是别人如何调用你的逻辑段落,参考OOAD思想)
做接口设计的时候我就想到,“T” 这个泛型,必须用extends做个约束,约束他必须是 Promise 的子类型
于是我就写出了如下代码
T extends Promise<P>
到这里思维就卡住了,于是我再次遍历所有 “关键词的语义”, 遍历到infer时我发现,“infer”的语义刚好是 “使用extends关键字时,extends右侧待推理的类型”
我再看向上面我写的代码 “Promise
” 刚好就在extends右侧!而且Promise
的 P 刚好就是我想要推理出的类型。
于是赶紧加上infer关键字
T extends Promise<infer P>
这题就解出来了。