类型检查 JavaScript 文件

以下是 .js 文件和 .ts 文件检查方式的一些显著区别。

属性从类主体中的赋值推断

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

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

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 注释构造函数中的声明以指定类型。即使它将在以后初始化,您甚至不必提供值。

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

构造函数等效于类

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

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 模块受支持

.js 文件中,TypeScript 理解 CommonJS 模块格式。对 exportsmodule.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 文件中是命名空间。这可以用来嵌套类,例如

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

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

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

它也可以用来创建简单的命名空间

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

其他变体也允许

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

对象字面量是开放式的

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

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

对象字面量表现得好像它们具有索引签名 [x:string]: any,允许它们被视为开放式映射而不是封闭式对象。

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

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 初始化的变量、参数或属性都将具有 any 类型,即使启用了严格的 null 检查。任何使用 [] 初始化的变量、参数或属性都将具有 any[] 类型,即使启用了严格的 null 检查。唯一的例外是具有多个初始化器的属性,如上所述。

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

函数参数默认情况下是可选的

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

需要注意的是,使用过多参数调用函数会导致错误。

例如

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 可选参数语法 ([ ]) 来表示可选性。例如:

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

arguments 的使用推断出的可变参数声明

如果函数体中包含对 arguments 引用,则该函数被隐式地认为具有可变参数 (即 (...arg: any[]) => any)。使用 JSDoc 可变参数语法来指定参数的类型。

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

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

在 extends 语句中

例如,React.Component 被定义为具有两个类型参数:PropsState。在 .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。

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

在函数调用中

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

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

要了解 JSDoc 中提供的所有功能,请参阅 参考文档

TypeScript 文档是一个开源项目。帮助我们改进这些页面 通过发送 Pull Request

此页面的贡献者
HNHarry Nguyen (51)
OTOrta Therox (17)
RCRyan Cavanaugh (3)
NSNathan Shively-Sanders (2)
MHMohamed Hegazy (2)
15+

上次更新:2024 年 3 月 21 日