Substitutability 中文含义是 可代替性,这个词我未在 TypeScript 的语言特性相关文档上看到,百度、谷歌搜索也寥寥无几。仅在TypeScript FAQ 找到相关描述。

有关类型系统的许多答案都提到了可替代性。 这是一个原则,即如果可以使用对象X代替某些对象Y,则XY的子类型。我们通常也说X可以分配给Y(这些术语在TypeScript中的含义略有不同,但是 区别在这里并不重要)。

这段描述很好理解,大体就是子类型可以用在父类型出现的地方。但实际涉及的TypeScript 使用场景,和这个词不是很契合,也许是语言的差异,中文含义不便于理解。

实际 Substitutability 解决的场景是:TypeScript 允许 function 作为回调函数时,入参个数、返回类型可以不符合方法签名。

回调 Function 入参比签名少

fetchResults 有一个参数,即回调函数。 该方法从某处获取数据,然后执行回调。 回调的方法签名有两个参数, statusCoderesults

function fetchResults(callback: (statusCode: number, results: number[]) => void) {
const results = [1,2,3];
...
callback(200, results);
}

我们用下面的方式调用 fetchResults,注意方法签名是不同的,它没有第二个参数 results

function handler(statusCode: number) {
// 业务处理
...
} fetchResults(handler); // ️

可以正常编译,没有任何错误或警告。 看起来有点奇怪,但细想一下,你一直在这么用。

Array.prototype.forEach 方法签名

    /**
* Performs the specified action for each element in an array.
* @param callbackfn A function that accepts up to three arguments. forEach calls the callbackfn function one time for each element in the array.
* @param thisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
*/
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void

实际使用:

let items = [1, 2, 3];
items.forEach(arg => console.log(arg));

在运行时,forEach使用三个参数(value、index、array)调用给定的回调函数,但大多数时候回调函数只使用其中的一个或两个参数。

那为什么不干脆将forEach 参数声明为可选。

forEach(callback: (element?: T, index?: number, array?: T[]))

如果声明为可选,由于回调的提供者不知道调用方何时会传递多少参数,将不得不检查各个参数,这显然不是你想要的。

function maybeCallWithArg(callback: (x?: number) => void) {
if (Math.random() > 0.5) {
callback();
} else {
callback(42);
}
}

声明非可选,是站在调用者的角度,保证按声明传递参数,可以兼容需要不同个数参数的回调函数;

这么处理的合理性在于,回调函数自身是最了解如何处理入参的,如果它不关心某些入参,它可以安全的忽略。

回调返回类型不匹配签名 return void

如果函数类型指定返回类型 void,则也接受具有不同的、更具体的返回类型的函数。同样,用前面的例子,这次增加handle的返回类型声明。

function handler(statusCode: number): {age:number}{
// 业务处理
...
return {"age": 4};
} fetchResults(handler); // ️

fetchResults 接受的回调函数返回类型是void,而这次的handler 返回{age:number}类型,依然正常编译。

你依然可以将callback结果赋值给一个变量,但仅仅限于声明语句,其他操作都将编译失败。

function fetchResults(callback: (statusCode: number, results: number[]) => void) {
const results = [1,2,3];
...
let didItWork = callback(200, results); // ️ 1)
console.log(didItWork); // ️
console.log(didItWork.age) //
didItWork = {"age": 4} ; //
} // 注意虽然编译报错,但不影响最后js执行,Playground 运行结果
[LOG]: {
"age": 4
}
[LOG]: 4

可能有人困惑既然void类型无法进行其他操作,为什么要允许1)处赋值void类型给变量。TypeScript 1.4语言规范 给出了如下说明:

注意:我们考虑过禁止声明Void类型的变量,因为它们没有用处。 但是,由于允许将Void作为泛型类型或函数的类型参数,因此不允许Void属性或参数是不可行的。

function foo<T>(param:T) {
let localParam:T = param;
}
foo<number>(3); // ️
foo<void>(undefined); // ️

这么处理的合理性在于,回调函数的调用者通过声明callback 返回void, 它最清楚也可以保证返回值不会被使用。

示例Playground

TypeScript 的 Substitutability的更多相关文章

  1. TypeScript: Angular 2 的秘密武器(译)

    本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch?v=e3djIqAGqZo 开场白 开场白主要分为三部分: 感谢了 ...

  2. TypeScript为Zepto编写LazyLoad插件

    平时项目中使用的全部是jQuery框架,但是对于做webapp来说jQuery太过于庞大,当然你可以选择jQuery 2.*针对移动端的版本. 这里我采用移动端使用率比较多的zepto框架,他跟jqu ...

  3. TypeScript Vs2013 下提示Can not compile modules unless '--module' flag is provided

    VS在开发TypeScript程序时候,如果import了模块有的时候会有如下提示: 这种情况下,只需要对当前TypeScript项目生成设置为AMD规范即可!

  4. TypeScript

    TypeScript: Angular 2 的秘密武器(译)   本文整理自Dan Wahlin在ng-conf上的talk.原视频地址: https://www.youtube.com/watch? ...

  5. 打造TypeScript的Visual Studio Code开发环境

    打造TypeScript的Visual Studio Code开发环境 本文转自:https://zhuanlan.zhihu.com/p/21611724 作者: 2gua TypeScript是由 ...

  6. 转职成为TypeScript程序员的参考手册

    写在前面 作者并没有任何可以作为背书的履历来证明自己写作这份手册的分量. 其内容大都来自于TypeScript官方资料或者搜索引擎获得,期间掺杂少量作者的私见,并会标明. 大部分内容来自于http:/ ...

  7. Webstorm编译TypeScript

    下载webstorm 下载node.js编译器npm   Webstorm的安装很简单.但如果没有Java For Mac 环境打开Webstorm时会有提示,点击提示会跳转下载链接,下载安装就好. ...

  8. CSS3与页面布局学习总结(七)——前端预处理技术(Less、Sass、CoffeeScript、TypeScript)

    CSS不像其它高级语言一样支持算术运算.变量.流程控制与面向对象特性,所以CSS样式较多时会引起一些问题,如修改复杂,冗余,某些别的语言很简单的功能实现不了等.而javascript则是一种半面向对象 ...

  9. 使用TypeScript拓展你自己的VS Code!

    0x00 前言 在前几天的美国纽约,微软举行了Connect(); //2015大会.通过这次大会,我们可以很高兴的看到微软的确变得更加开放也更加务实了.当然,会上放出了不少新产品和新功能,其中就包括 ...

随机推荐

  1. ORB-SLAM3 细读单目初始化过程(上)

    作者:乔不思 来源:微信公众号|3D视觉工坊(系投稿) 3D视觉精品文章汇总:https://github.com/qxiaofan/awesome-3D-Vision-Papers/ 点击上方&qu ...

  2. JS内存

    内存是用来存什么的 通俗的来说呢,就是用来存 var let function const 声明的变量. 内存的大小 与操作系统有关,64位1.4G 32位0.7G. 为啥内存大小要这么设计,为啥不是 ...

  3. 杭电OJ----1097:一个难题(c++)

    问题描述 lcy给feng5166,lwg,JGShining和Ignatius带来了一个难题:给了a和b,如何知道a ^ b.每个人都反对这个BT问题,所以lcy使问题比开始容易. 这个难题描述了: ...

  4. Istio 知多少 | 下一代微服务的守护者

    1. 引言 在写完eShopOnContainers 知多少[12]:Envoy gateways后,就一直想进一步探索Service Mesh,最近刚在极客时间上学完<Service Mesh ...

  5. go语言实现99乘法表

    Go语言学习笔记(99乘法表)-day01 通过for循环实现99乘法表 预期效果 源代码 for x :=1;x<10;x++{ for y :=1;x>=y;y++{ fmt.Prin ...

  6. 结合MATLAB、Python、R语言,在求得显著差异的边(节点对)之后,怎么画circle图

                                                            先来看看成果图: OK,开始画图: 实验背景声明:在脑影像分析中,我们首先构建脑网络,然 ...

  7. 白日梦的Elasticsearch笔记(一)基础篇

    目录 一.导读 1.1.认识ES 1.2.安装.启动ES.Kibana.IK分词器 二.核心概念 2.1.Near Realtime (NRT) 2.2.Cluster 2.3.Node 2.4.In ...

  8. 利用GPU实现大规模动画角色的渲染(转)

    原文: https://www.cnblogs.com/murongxiaopifu/p/7250772.html 利用GPU实现大规模动画角色的渲染 0x00 前言 我想很多开发游戏的小伙伴都希望自 ...

  9. Tengine 四层代理:

    Tengine 四层代理: 1 ) 安装tengine ( nginx1.9 以上版本 编译以后要支持stream 模块) 1.1 ) tengine(nginx) 一定要是nginx-1.9.X 以 ...

  10. 关于使用jq跨域请求的实现

    今天算是把js跨域请求搞定了,想实现跨越,首先想到的是JSONP,但是具体去做的时候,发现有很多坑.在本地测试好之后又发现目标网站做了https证书认证,也就是实用的jsonp请求地址必须是https ...