Skip to content
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待
虚位以待

检查 JavaScript 文件

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

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

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

.js 文件中,编译器根据类体内的属性赋值来推断属性。 属性的类型是在构造函数中给出的类型,除非它没有在那里定义,或者构造函数中的类型是 undefinednull。 在这种情况下,类型就是这些赋值中所有右侧值的联合类型。 在构造函数中定义的属性总是被认为存在,而仅在方法、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 模块格式。 对 exportsmodule.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[] 类型

任何用 nullundefined 初始化的变量、参数或属性都将具有 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 被定义为具有两个类型参数 PropsState。 在 .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 中所有可用功能,请参阅参考文档