類型檢查 JavaScript 檔案

以下是 .js 檔案與 .ts 檔案中檢查運作方式的一些顯著差異。

根據類別主體中的指派推論屬性

ES2015 沒有宣告類別屬性的方法。屬性會動態指派,就像物件文字一樣。

.js 檔案中,編譯器會根據類別主體內的屬性指派推論屬性。屬性的型別是建構函式中提供的型別,除非建構函式中未定義,或建構函式中的型別未定義或為 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'.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 推斷出的 Var-args 參數宣告

函式主體具有對 arguments 參照的參照,會被隱含地視為具有 var-arg 參數 (即 (...arg: any[]) => any)。使用 JSDoc var-arg 語法來指定參數的類型。

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 日