以下是关于 .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 文件中,编译器会从类体内的属性赋值中推断属性类型。属性的类型是构造函数中给定的类型,除非构造函数中没有定义,或构造函数中的类型为 undefined 或 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.
jsTryclassC {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 注释来指定类型。如果属性稍后会被初始化,你甚至不必赋值:
🌐 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:
jsTryclassC {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";
构造函数等同于类
🌐 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.
jsTryfunctionC () {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 模块
🌐 CommonJS modules are supported
在 .js 文件中,TypeScript 能理解 CommonJS 模块格式。
对 exports 和 module.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:
jsTryclassC {}C .D = class {};
而且,对于 ES2015 之前的代码,它可以用来模拟静态方法:
🌐 And, for pre-ES2015 code, it can be used to simulate static methods:
jsTryfunctionOuter () {this.y = 2;}Outer .Inner = function () {this.yy = 2;};Outer .Inner ();
它还可以用于创建简单的命名空间:
🌐 It can also be used to create simple namespaces:
jsTryvarns = {};ns .C = class {};ns .func = function () {};ns ;
其他变体也是允许的:
🌐 Other variants are allowed as well:
jsTry// IIFEvarns = (function (n ) {returnn || {};})();ns .CONST = 1;// defaulting to globalvarassign =assign ||function () {// code goes here};assign .extra = 1;
对象字面量是开放式的
🌐 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:
jsTryvarobj = {a : 1 };obj .b = 2; // Allowed
对象字面量的行为就像它们具有一个索引签名 [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:
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, and empty array initializers are of type any or any[]
任何用 null 或 undefined 初始化的变量、参数或属性,其类型都会是 any,即使开启了严格的空值检查。任何用 [] 初始化的变量、参数或属性,其类型都会是 any[],即使开启了严格的空值检查。唯一的例外是前面所述具有多个初始化器的属性。
🌐 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.
jsTryfunctionFoo (i = null) {if (!i )i = 1;varj =undefined ;j = 2;this.l = [];}varfoo = newFoo ();foo .l .push (foo .i );foo .l .push ("end");
函数参数默认可选
🌐 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:
jsTryfunctionbar (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 可选参数语法([ ])来表示可选性。例如:
🌐 JSDoc annotated functions are excluded from this rule.
Use JSDoc optional parameter syntax ([ ]) to express optionality. e.g.:
jsTry/*** @param {string} [somebody] - Somebody's name.*/functionsayHello (somebody ) {if (!somebody ) {somebody = "John Doe";}console .log ("Hello " +somebody );}sayHello ();
从 arguments 的使用中推断出的可变参数声明
🌐 Var-args parameter declaration inferred from use of arguments
函数体中引用了 arguments 的函数会被隐式认为有一个可变参数(即 (...arg: any[]) => any)。使用 JSDoc 可变参数语法来指定参数的类型。
🌐 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.
jsTry/** @param {...number} args */functionsum (/* numbers */) {vartotal = 0;for (vari = 0;i <arguments .length ;i ++) {total +=arguments [i ];}returntotal ;}
未指定类型参数默认为 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 定义了两个类型参数,Props 和 State。在一个 .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:
jsimport { 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:
jsimport { 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:
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
在函数调用中
🌐 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:
jsvar p = new Promise((resolve, reject) => {reject();});p; // Promise<any>;
要了解 JSDoc 中提供的所有功能,请参见 参考。
🌐 To learn all of the features available in JSDoc, see the reference.