typescript 函数参数类型不做检查

tech2023-02-06  92

typescript 函数参数类型不做检查

请问typescript会检查参数类型么?

https://www.zhihu.com/question/333285875/answer/742095444

 

Lcng

放弃的时候,明白了坚持的意义。

TypeScript(编译器)会检查参数类型,但只会检查参数(以及变量和对象成员)的编译时类型,而不会(也无法)检查参数的运行时类型。

TypeScript中的编译时类型是基于结构的(若要了解基于结构的类型系统,请参考https://en.wikipedia.org/wiki/Structural_type_system),因此TypeScript中两个对象如果拥有一样的成员,那么这两个对象就会被认为拥有相同的类型。

比如我们有一个产品类Product(它可能跟你的产品类不一样,但这不影响它将发挥的作用):

export class Product { name: string; // 产品名字 weight: number; // 产品重量 }

接着我们定义了一个产品数组products,并理所当然地向这个数组填充了两个产品对象:

let products: Array<Product> = new Array<Product>(); products.push(new Product()); products.push(new Product());

现在我们想知道数组products中的两个成员的类型是不是产品类Product,因此我们做了如下检测:

for (const product of products) { console.log(product instanceof Product); // 输出true }

以上代码会输出两个true,一切都是那么的恰到好处。

但有时候事情好像会失去掌控,比如我们又定义了另一个产品数组productList,同时也为它填充了两个产品对象:

let productList: Array<Product> = new Array<Product>(); productList.push({name: '鼠标', weight: 20}); productList.push({name: '键盘', weight: 50});

以上代码将两个对象字面量(Object Literal)添加到了产品数组productList中,而这两个对象字面量都不是通过产品类Product构造的,因此它们的类型应该不是产品类Product,也就不能被添加到产品数组productList中。

但是事实上并非如此,TypeScript中对象的编译时类型是由对象的结构决定的,以上两个对象字面量都拥有string类型的name属性和number的weight属性,而这两个属性以及它们各自的类型则构成了这两个对象字面量的编译时类型,即{ name: string, weight: number }——这被称为类型字面量(Type Literal)。

这个类型字面量对我们来说是不是有些眼熟呢?是的,产品类Product所代表的类型正好也拥有同样的结构,因此对象字面量{ name: '鼠标', weight: 20 }和通过new Product()构造的对象具备同样的编译时类型,它们都能被添加到编译时类型为Array<Product>的产品数组中。

以上是有关TypeScript中的编译时类型的简单解释,而这一解释也只回答了问题的一半;剩下的一半,还需要通过JavaScript中的运行时类型来解释。

为什么这么说呢?因为TypeScript中的编译时类型在TypeScript代码被编译成JavaScript之后会消失,在JavaScript中我们需要关心的是运行时类型。

比如前面定义商品数组products的TypeScript代码:

let products: Array<Product> = new Array<Product>();

它被编译之后是这样的JavaScript代码:

var products = new Array();

从以上编译过程中可以看出,编译后商品数组products的编译时类型Array<Product>消失了。那么,此时商品数组products还有类型吗?

答案是有,当它被加载到Node.js或浏览器中运行时,它有一个运行时类型。而为了获得这个运行时类型,我们需要使用操作符typeof。

为了验证这一点,我们可以运行以下代码:

console.log(typeof(products)); // 输出object

以上代码将在运行时输出字符串object,而object就是产品数组products的运行时类型——变量在TypeScript中的编译时类型会在被编译成JavaScript之后消失,此时它只有运行时类型。

这样的编译时类型丢失同样会发生在产品数组products和productList的成员上:它们在TypeScript中的编译时类型都是产品类Product(还记得为什么吗),但在编译后的JavaScript中的运行时类型都成了object。

显然,有时候仅仅是知道一个对象的运行时类型是object并不够,我们经常会需要知道指定的对象是通过那个构造函数(类)构造的,而这时我们会使用操作符instanceof。

正是因为操作符instanceof的这一作用,所以前面提到的以下代码会输出两个true:

for (const product of products) { console.log(product instanceof Product); // 输出true }

而如果我们继续执行以下代码,我们则会得到两个false,因为商品数组productList中的成员不是通过产品类Product构造的(它们是对象字面量):

for (const product of productList) { console.log(product instanceof Product); // 输出false }

以上两个不同的输出会让我们觉得TypeScript中的类型检查失去了作用,但事实是我们对TypeScript的类型检查期望“过高”,因为TypeScript只能进行编译时类型检查,而编译时类型相同的对象并不一定都是通过同一个类构造的——TypeScript中的类型兼容是基于类型的结构判断的。

当然,我们肯定希望能够尽量避免“编译时类型相同的对象不一定是通过同一个类构造的”这种情况发生。而为了实现这样的避免,我们可以结合TypeScript中的装饰器和泛型,创建一个通用的对象工厂。

此外,当判断一个对象是否是由一个指定的构造函数(类)构造的时,有时候操作符instanceof会表现的有些“怪异”,这时我们需要使用对象的constructor属性——若要了解更多,请仔细阅读《JavaScript高级程序设计》,[美] Nicholas C.Zakas 著,李松峰,曹力译。

最后,TypeScript中的(编译时)类型略有“复杂”,若要了解更多,请仔细阅读其官方文档。

发布于 2019-07-08

继续浏览内容

知乎

发现更大的世界

打开

浏览器

继续

更多回答

百折不挠的喵喵

无他,唯友众也。

编译前叫typescript,编译后就是ES5678

了。所以编译时查类型,运行时不会查,只要能跑就绝不报错。

发布于 2019-07-04

继续浏览内容

知乎

发现更大的世界

打开

浏览器

继续

天下一

只是一粒渺小的微尘

如果猜得没错,你的product只是一个JSON对象,而不是一个new出来的实例。

最新回复(0)