类型检查 JavaScript 文件

以下是 .js 文件与 .ts 文件的检查工作方式的一些显着差异。

¥Here are some notable differences on how checking works in .js files compared to .ts files.

属性是从类主体中的分配中推断出来的

¥Properties are inferred from assignments in class bodies

ES2015 没有在类上声明属性的方法。属性是动态分配的,就像对象字面量一样。

¥ES2015 does not have a means for declaring properties on classes. Properties are dynamically assigned, just like object literals.

.js 文件中,编译器从类体内的属性分配中推断出属性。属性的类型是构造函数中给定的类型,除非它未在此处定义,或者构造函数中的类型未定义或为 null。在这种情况下,类型是这些赋值中所有右侧值的类型的并集。在构造函数中定义的属性始终假定存在,而仅在方法、getter 或 setter 中定义的属性被认为是可选的。

¥In a .js file, the compiler infers properties from property assignments inside the class body. The type of a property is the type given in the constructor, unless it’s not defined there, or the type in the constructor is undefined or null. In that case, the type is the union of the types of all the right-hand values in these assignments. Properties defined in the constructor are always assumed to exist, whereas ones defined just in methods, getters, or setters are considered optional.

js
class C {
constructor() {
this.constructorOnly = 0;
this.constructorUnknown = undefined;
}
method() {
this.constructorOnly = false;
Type 'boolean' is not assignable to type 'number'.2322Type 'boolean' is not assignable to type 'number'.
this.constructorUnknown = "plunkbat"; // ok, constructorUnknown is string | undefined
this.methodOnly = "ok"; // ok, but methodOnly could also be undefined
}
method2() {
this.methodOnly = true; // also, ok, methodOnly's type is string | boolean | undefined
}
}
Try

如果属性从未在类主体中设置,则它们被认为是未知的。如果你的类具有只能读取的属性,请使用 JSDoc 在构造函数中添加然后注释声明以指定类型。如果稍后将初始化它,你甚至不必提供值:

¥If properties are never set in the class body, they are considered unknown. If your class has properties that are only read from, add and then annotate a declaration in the constructor with JSDoc to specify the type. You don’t even have to give a value if it will be initialized later:

js
class C {
constructor() {
/** @type {number | undefined} */
this.prop = undefined;
/** @type {number | undefined} */
this.count;
}
}
 
let c = new C();
c.prop = 0; // OK
c.count = "string";
Type 'string' is not assignable to type 'number'.2322Type 'string' is not assignable to type 'number'.
Try

构造函数等同于类

¥Constructor functions are equivalent to classes

在 ES2015 之前,JavaScript 使用构造函数而不是类。编译器支持这种模式并将构造函数理解为等同于 ES2015 类。上述属性推断规则的工作方式完全相同。

¥Before ES2015, JavaScript used constructor functions instead of classes. The compiler supports this pattern and understands constructor functions as equivalent to ES2015 classes. The property inference rules described above work exactly the same way.

js
function C() {
this.constructorOnly = 0;
this.constructorUnknown = undefined;
}
C.prototype.method = function () {
this.constructorOnly = false;
Type 'boolean' is not assignable to type 'number'.2322Type 'boolean' is not assignable to type 'number'.
this.constructorUnknown = "plunkbat"; // OK, the type is string | undefined
};
Try

支持 CommonJS 模块

¥CommonJS modules are supported

.js 文件中,TypeScript 理解 CommonJS 模块格式。exportsmodule.exports 的分配被视为导出声明。同样,require 函数调用被识别为模块导入。例如:

¥In a .js file, TypeScript understands the CommonJS module format. Assignments to exports and module.exports are recognized as export declarations. Similarly, require function calls are recognized as module imports. For example:

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 的模块支持更宽容。支持大多数赋值和声明的组合。

¥The module support in JavaScript is much more syntactically forgiving than TypeScript’s module support. Most combinations of assignments and declarations are supported.

类、函数和对象字面量都是命名空间

¥Classes, functions, and object literals are namespaces

类是 .js 文件中的命名空间。这可用于嵌套类,例如:

¥Classes are namespaces in .js files. This can be used to nest classes, for example:

js
class C {}
C.D = class {};
Try

而且,对于 ES2015 之前的代码,它可以用来模拟静态方法:

¥And, for pre-ES2015 code, it can be used to simulate static methods:

js
function Outer() {
this.y = 2;
}
 
Outer.Inner = function () {
this.yy = 2;
};
 
Outer.Inner();
Try

它还可以用于创建简单的命名空间:

¥It can also be used to create simple namespaces:

js
var ns = {};
ns.C = class {};
ns.func = function () {};
 
ns;
Try

其他变体也是允许的:

¥Other variants are allowed as well:

js
// IIFE
var ns = (function (n) {
return n || {};
})();
ns.CONST = 1;
 
// defaulting to global
var assign =
assign ||
function () {
// code goes here
};
assign.extra = 1;
Try

对象字面量是开放式的

¥Object literals are open-ended

.ts 文件中,初始化变量声明的对象字面将其类型赋予声明。不能添加原始字面中未指定的新成员。.js 文件放宽了这个规则;对象字面具有开放式类型(索引签名),允许添加和查找最初未定义的属性。例如:

¥In a .ts file, an object literal that initializes a variable declaration gives its type to the declaration. No new members can be added that were not specified in the original literal. This rule is relaxed in a .js file; object literals have an open-ended type (an index signature) that allows adding and looking up properties that were not defined originally. For instance:

js
var obj = { a: 1 };
obj.b = 2; // Allowed
Try

对象字面的行为就像它们具有索引签名 [x:string]: any 一样,允许将它们视为开放映射而不是封闭对象。

¥Object literals behave as if they have an index signature [x:string]: any that allows them to be treated as open maps instead of closed objects.

与其他特殊的 JS 检查行为一样,可以通过为变量指定 JSDoc 类型来更改此行为。例如:

¥Like other special JS checking behaviors, this behavior can be changed by specifying a JSDoc type for the variable. For example:

js
/** @type {{a: number}} */
var obj = { a: 1 };
obj.b = 2;
Property 'b' does not exist on type '{ a: number; }'.2339Property 'b' does not exist on type '{ a: number; }'.
Try

null、undefined、以及空数组初始值设定项的类型为 any 或 any[]

¥null, undefined, and empty array initializers are of type any or any[]

任何使用 null 或 undefined 初始化的变量、参数或属性都将具有 any 类型,即使启用了严格的 null 检查也是如此。使用 [] 初始化的任何变量、参数或属性都将具有任何 [] 类型,即使启用了严格的空检查也是如此。唯一的例外是具有多个初始值设定项的属性,如上所述。

¥Any variable, parameter or property that is initialized with null or undefined will have type any, even if strict null checks is turned on. Any variable, parameter or property that is initialized with [] will have type any[], even if strict null checks is turned on. The only exception is for properties that have multiple initializers as described above.

js
function Foo(i = null) {
if (!i) i = 1;
var j = undefined;
j = 2;
this.l = [];
}
 
var foo = new Foo();
foo.l.push(foo.i);
foo.l.push("end");
Try

函数参数默认可选

¥Function parameters are optional by default

由于无法在 ES2015 之前的 JavaScript 中指定参数的可选性,因此 .js 文件中的所有函数参数都被认为是可选的。允许参数少于声明的参数数量的调用。

¥Since there is no way to specify optionality on parameters in pre-ES2015 JavaScript, all function parameters in .js file are considered optional. Calls with fewer arguments than the declared number of parameters are allowed.

重要的是要注意调用带有太多参数的函数是错误的。

¥It is important to note that it is an error to call a function with too many arguments.

例如:

¥For instance:

js
function bar(a, b) {
console.log(a + " " + b);
}
 
bar(1); // OK, second argument considered optional
bar(1, 2);
bar(1, 2, 3); // Error, too many arguments
Expected 0-2 arguments, but got 3.2554Expected 0-2 arguments, but got 3.
Try

JSDoc 注释函数被排除在这个规则之外。使用 JSDoc 可选参数语法 ([ ]) 来表达可选性。例如。:

¥JSDoc annotated functions are excluded from this rule. Use JSDoc optional parameter syntax ([ ]) to express optionality. e.g.:

js
/**
 
* @param {string} [somebody] - Somebody's name.
*/
function sayHello(somebody) {
if (!somebody) {
somebody = "John Doe";
}
console.log("Hello " + somebody);
}
 
sayHello();
Try

从使用 arguments 推断出的 Var-args 参数声明

¥Var-args parameter declaration inferred from use of arguments

其函数体引用 arguments 引用的函数被隐式地视为具有 var-arg 参数(即 (...arg: any[]) => any)。使用 JSDoc var-arg 语法指定参数的类型。

¥A function whose body has a reference to the arguments reference is implicitly considered to have a var-arg parameter (i.e. (...arg: any[]) => any). Use JSDoc var-arg syntax to specify the type of the arguments.

js
/** @param {...number} args */
function sum(/* numbers */) {
var total = 0;
for (var i = 0; i < arguments.length; i++) {
total += arguments[i];
}
return total;
}
Try

未指定的类型参数默认为 any

¥Unspecified type parameters default to any

由于在 JavaScript 中没有用于指定泛型类型参数的自然语法,因此未指定的类型参数默认为 any

¥Since there is no natural syntax for specifying generic type parameters in JavaScript, an unspecified type parameter defaults to any.

在扩展子句中

¥In extends clause

例如,React.Component 被定义为具有两个类型参数,PropsState。在 .js 文件中,没有合法的方式在 extends 子句中指定这些。默认情况下,类型参数将为 any

¥For instance, React.Component is defined to have two type parameters, Props and State. In a .js file, there is no legal way to specify these in the extends clause. By default the type arguments will be any:

js
import { Component } from "react";
class MyComponent extends Component {
render() {
this.props.b; // Allowed, since this.props is of type any
}
}

使用 JSDoc @augments 明确指定类型。例如:

¥Use JSDoc @augments to specify the types explicitly. for instance:

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 参考资料中

¥In JSDoc references

JSDoc 中未指定的类型参数默认为任何:

¥An unspecified type argument in JSDoc defaults to any:

js
/** @type{Array} */
var x = [];
 
x.push(1); // OK
x.push("string"); // OK, x is of type Array<any>
 
/** @type{Array.<number>} */
var y = [];
 
y.push(1); // OK
y.push("string"); // Error, string is not assignable to number
Try

在函数调用中

¥In function calls

对泛型函数的调用使用参数来推断类型参数。有时这个过程无法推断任何类型,主要是因为缺乏推断来源;在这些情况下,类型参数将默认为 any。例如:

¥A call to a generic function uses the arguments to infer the type parameters. Sometimes this process fails to infer any types, mainly because of lack of inference sources; in these cases, the type parameters will default to any. For example:

js
var p = new Promise((resolve, reject) => {
reject();
});
p; // Promise<any>;

要了解 JSDoc 中可用的所有功能,请参阅 参考资料

¥To learn all of the features available in JSDoc, see the reference.