以下是 .js
文件和 .ts
文件检查方式的一些显著区别。
属性从类主体中的赋值推断
ES2015 没有为在类上声明属性提供方法。属性是动态分配的,就像对象字面量一样。
在 .js
文件中,编译器从类主体内的属性赋值推断属性。属性的类型是在构造函数中给定的类型,除非它没有在那里定义,或者构造函数中的类型是未定义或空。在这种情况下,类型是所有这些赋值中右侧值的类型的并集。在构造函数中定义的属性始终被假定为存在,而仅在方法、getter 或 setter 中定义的属性被认为是可选的。
jsTry
classC {constructor() {this.constructorOnly = 0;this.constructorUnknown =undefined ;}method () {this.Type 'boolean' is not assignable to type 'number'.2322Type 'boolean' is not assignable to type 'number'.constructorOnly = false;this.constructorUnknown = "plunkbat"; // ok, constructorUnknown is string | undefinedthis.methodOnly = "ok"; // ok, but methodOnly could also be undefined}method2 () {this.methodOnly = true; // also, ok, methodOnly's type is string | boolean | undefined}}
如果属性从未在类主体中设置,则它们被认为是未知的。如果您的类具有仅从其读取的属性,请添加并使用 JSDoc 注释构造函数中的声明以指定类型。即使它将在以后初始化,您甚至不必提供值。
jsTry
classC {constructor() {/** @type {number | undefined} */this.prop =undefined ;/** @type {number | undefined} */this.count ;}}letc = newC ();c .prop = 0; // OKType 'string' is not assignable to type 'number'.2322Type 'string' is not assignable to type 'number'.c .count = "string";
构造函数等效于类
在 ES2015 之前,JavaScript 使用构造函数而不是类。编译器支持这种模式,并将构造函数理解为等效于 ES2015 类。上面描述的属性推断规则以完全相同的方式工作。
jsTry
functionC () {this.constructorOnly = 0;this.constructorUnknown =undefined ;}C .prototype .method = function () {this.Type 'boolean' is not assignable to type 'number'.2322Type 'boolean' is not assignable to type 'number'.constructorOnly = false;this.constructorUnknown = "plunkbat"; // OK, the type is string | undefined};
CommonJS 模块受支持
在 .js
文件中,TypeScript 理解 CommonJS 模块格式。对 exports
和 module.exports
的赋值被识别为导出声明。类似地,require
函数调用被识别为模块导入。例如
js
// same as `import module "fs"`const fs = require("fs");// same as `export function readFile`module.exports.readFile = function (f) {return fs.readFileSync(f);};
JavaScript 中的模块支持在语法上比 TypeScript 的模块支持宽松得多。大多数赋值和声明的组合都受支持。
类、函数和对象字面量是命名空间
类在 .js
文件中是命名空间。这可以用来嵌套类,例如
jsTry
classC {}C .D = class {};
并且,对于 ES2015 之前的代码,它可以用来模拟静态方法
jsTry
functionOuter () {this.y = 2;}Outer .Inner = function () {this.yy = 2;};Outer .Inner ();
它也可以用来创建简单的命名空间
jsTry
varns = {};ns .C = class {};ns .func = function () {};ns ;
其他变体也允许
jsTry
// IIFEvarns = (function (n ) {returnn || {};})();ns .CONST = 1;// defaulting to globalvarassign =assign ||function () {// code goes here};assign .extra = 1;
对象字面量是开放式的
在 .ts
文件中,用于初始化变量声明的对象字面量会将它的类型赋予声明。不能添加在原始字面量中未指定的任何新成员。此规则在 .js
文件中有所放宽;对象字面量具有开放式类型(索引签名),允许添加和查找最初未定义的属性。例如
jsTry
varobj = {a : 1 };obj .b = 2; // Allowed
对象字面量表现得好像它们具有索引签名 [x:string]: any
,允许它们被视为开放式映射而不是封闭式对象。
与其他特殊的 JS 检查行为一样,可以通过为变量指定 JSDoc 类型来更改此行为。例如
jsTry
/** @type {{a: number}} */varobj = {a : 1 };Property 'b' does not exist on type '{ a: number; }'.2339Property 'b' does not exist on type '{ a: number; }'.obj .= 2; b
null、undefined 和空数组初始化器类型为 any 或 any[]
任何使用 null 或 undefined 初始化的变量、参数或属性都将具有 any 类型,即使启用了严格的 null 检查。任何使用 [] 初始化的变量、参数或属性都将具有 any[] 类型,即使启用了严格的 null 检查。唯一的例外是具有多个初始化器的属性,如上所述。
jsTry
functionFoo (i = null) {if (!i )i = 1;varj =undefined ;j = 2;this.l = [];}varfoo = newFoo ();foo .l .push (foo .i );foo .l .push ("end");
函数参数默认情况下是可选的
由于在 ES2015 之前的 JavaScript 中无法指定参数的可选性,因此 .js
文件中所有函数参数都被视为可选的。允许调用参数数量少于声明参数数量的函数。
需要注意的是,使用过多参数调用函数会导致错误。
例如
jsTry
functionbar (a ,b ) {console .log (a + " " +b );}bar (1); // OK, second argument considered optionalbar (1, 2);Expected 0-2 arguments, but got 3.2554Expected 0-2 arguments, but got 3.bar (1, 2,3 ); // Error, too many arguments
JSDoc 注释的函数不包含在此规则中。使用 JSDoc 可选参数语法 ([
]
) 来表示可选性。例如:
jsTry
/*** @param {string} [somebody] - Somebody's name.*/functionsayHello (somebody ) {if (!somebody ) {somebody = "John Doe";}console .log ("Hello " +somebody );}sayHello ();
从 arguments
的使用推断出的可变参数声明
如果函数体中包含对 arguments
引用,则该函数被隐式地认为具有可变参数 (即 (...arg: any[]) => any
)。使用 JSDoc 可变参数语法来指定参数的类型。
jsTry
/** @param {...number} args */functionsum (/* numbers */) {vartotal = 0;for (vari = 0;i <arguments .length ;i ++) {total +=arguments [i ];}returntotal ;}
未指定的类型参数默认值为 any
由于 JavaScript 中没有用于指定泛型类型参数的自然语法,因此未指定的类型参数默认值为 any
。
在 extends 语句中
例如,React.Component
被定义为具有两个类型参数:Props
和 State
。在 .js
文件中,没有合法的方式在 extends 语句中指定这些参数。默认情况下,类型参数将为 any
。
js
import { Component } from "react";class MyComponent extends Component {render() {this.props.b; // Allowed, since this.props is of type any}}
使用 JSDoc @augments
来显式指定类型。例如:
js
import { Component } from "react";/*** @augments {Component<{a: number}, State>}*/class MyComponent extends Component {render() {this.props.b; // Error: b does not exist on {a:number}}}
在 JSDoc 引用中
JSDoc 中未指定的类型参数默认值为 any。
jsTry
/** @type{Array} */varx = [];x .push (1); // OKx .push ("string"); // OK, x is of type Array<any>/** @type{Array.<number>} */vary = [];y .push (1); // OKy .push ("string"); // Error, string is not assignable to number
在函数调用中
对泛型函数的调用使用参数来推断类型参数。有时此过程无法推断出任何类型,主要是因为缺乏推断来源;在这些情况下,类型参数将默认为 any
。例如:
js
var p = new Promise((resolve, reject) => {reject();});p; // Promise<any>;
要了解 JSDoc 中提供的所有功能,请参阅 参考文档。