Skip to content
赞助商
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

检查 JavaScript 文件 ​

这里列举了在 .js 文件中检查工作方式与 .ts 文件相比的一些显著差异。

属性是根据类体内的赋值推断的 ​

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

在 .js 文件中,编译器根据类体内的属性赋值来推断属性。 属性的类型是在构造函数中给出的类型,除非它没有在那里定义,或者构造函数中的类型是 undefined 或 null。 在这种情况下,类型就是这些赋值中所有右侧值的联合类型。 在构造函数中定义的属性总是被认为存在,而仅在方法、getter 或 setter 中定义的属性则被认为是可选的。

js
class 
C
{
constructor() { this.
constructorOnly
= 0;
this.
constructorUnknown
=
undefined
;
}
method
() {
this.constructorOnly = false;
Type 'boolean' is not assignable to type 'number'.
this.
constructorUnknown
= "plunkbat"; // ok, constructorUnknown 的类型是 string | undefined
this.
methodOnly
= "ok"; // ok, 但 methodOnly 也可能是 undefined
}
method2
() {
this.
methodOnly
= true; // 同样 ok, methodOnly 的类型是 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'.
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'.
this.
constructorUnknown
= "plunkbat"; // OK, 类型是 string | undefined
};
Try

支持 CommonJS 模块 ​

在 .js 文件中,TypeScript 理解 CommonJS 模块格式。 对 exports 和 module.exports 的赋值被识别为导出声明。 类似地,require 函数调用被识别为模块导入。例如:

js
// 等同于 `import module "fs"`
const fs = require("fs");

// 等同于 `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;
// 默认为全局 var
assign
=
assign
||
function () { // 代码放在这里 };
assign
.
extra
= 1;
Try

对象字面量是开放式的 ​

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

js
var 
obj
= {
a
: 1 };
obj
.b = 2; // 允许
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; }'.
Try

null、undefined 和空数组初始化器是 any 或 any[] 类型 ​

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

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,第二个参数被认为是可选的
bar
(1, 2);
bar
(1, 2, 3); // 错误,参数过多
Expected 0-2 arguments, but got 3.
Try

JSDoc 注释的函数不在此规则内。 使用 JSDoc 可选参数语法([ ])来表示可选性。例如:

js
/**
 * @param {string} [somebody] - 某人的名字。
 */
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 被定义为具有两个类型参数 Props 和 State。 在 .js 文件中,没有合法的方法在 extends 子句中指定这些。默认情况下,类型参数将是 any:

js
import { Component } from "react";

class MyComponent extends Component {
  render() {
    this.props.b; // 允许,因为 this.props 是 any 类型
  }
}

使用 JSDoc @augments 来显式指定类型。例如:

js
import { Component } from "react";

/**
 * @augments {Component<{a: number}, State>}
 */
class MyComponent extends Component {
  render() {
    this.props.b; // 错误:{a:number} 上不存在 b
  }
}

在 JSDoc 引用中 ​

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

js
/** @type{Array} */
var 
x
= [];
x
.
push
(1); // OK
x
.
push
("string"); // OK, x 是 Array<any> 类型
/** @type{Array.<number>} */ var
y
= [];
y
.
push
(1); // OK
y
.
push
("string"); // 错误,string 不能赋值给 number
Try

在函数调用中 ​

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

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

p; // Promise<any>;

要了解 JSDoc 中所有可用功能,请参阅参考文档。