以下是在 .js 檔案中進行型別檢查與在 .ts 檔案中相比的一些顯著差異。
屬性是從類體中的賦值推斷出來的
ES2015 沒有在類上宣告屬性的方法。屬性是動態分配的,就像物件字面量一樣。
在 .js 檔案中,編譯器從類體內的屬性賦值中推斷屬性。屬性的型別是在建構函式中給出的型別,除非它沒有在建構函式中定義,或者建構函式中的型別為 undefined 或 null。在這種情況下,型別是這些賦值中所有右側值的型別的並集。建構函式中定義的屬性被假定為始終存在,而僅在方法、getter 或 setter 中定義的屬性則被視為可選的。
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}}
如果屬性從未在類體中設定,則它們被視為未知(unknown)。如果你的類具有僅被讀取的屬性,請在建構函式中新增並使用 JSDoc 註解宣告以指定型別。如果以後會進行初始化,你甚至不需要為其賦初始值。
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";
建構函式等同於類
在 ES2015 之前,JavaScript 使用建構函式而不是類。編譯器支援這種模式,並將構造函數理解為等同於 ES2015 類。上述屬性推斷規則以完全相同的方式工作。
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 模組
在 .js 檔案中,TypeScript 理解 CommonJS 模組格式。對 exports 和 module.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 檔案中是名稱空間。這可以用來巢狀類,例如:
jsTryclassC {}C .D = class {};
此外,對於 ES2015 之前的程式碼,它可用於模擬靜態方法:
jsTryfunctionOuter () {this.y = 2;}Outer .Inner = function () {this.yy = 2;};Outer .Inner ();
它還可以用於建立簡單的名稱空間:
jsTryvarns = {};ns .C = class {};ns .func = function () {};ns ;
其他變體也是允許的:
jsTry// IIFEvarns = (function (n ) {returnn || {};})();ns .CONST = 1;// defaulting to globalvarassign =assign ||function () {// code goes here};assign .extra = 1;
物件字面量是開放式的
在 .ts 檔案中,初始化變數宣告的物件字面量會將型別賦予該宣告。不能新增原始字面量中未指定的成員。此規則在 .js 檔案中放寬了;物件字面量具有開放式型別(索引簽名),允許新增和查詢原始定義中未包含的屬性。例如:
jsTryvarobj = {a : 1 };obj .b = 2; // Allowed
物件字面量的行為就像它們具有索引簽名 [x:string]: any 一樣,這允許它們被視為開放對映(open maps)而不是封閉物件。
與其他特殊的 JS 檢查行為一樣,此行為可以透過為變數指定 JSDoc 型別來更改。例如:
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 初始化的變數、引數或屬性都將具有型別 any,即使啟用了嚴格空值檢查(strict null checks)。任何使用 [] 初始化的變數、引數或屬性都將具有型別 any[],即使啟用了嚴格空值檢查。唯一的例外是如上所述具有多個初始化程式的屬性。
jsTryfunctionFoo (i = null) {if (!i )i = 1;varj =undefined ;j = 2;this.l = [];}varfoo = newFoo ();foo .l .push (foo .i );foo .l .push ("end");
函式引數預設是可選的
由於在 ES2015 之前的 JavaScript 中沒有指定引數可選性的方法,因此 .js 檔案中的所有函式引數都被視為可選的。允許以少於宣告引數數量的引數進行呼叫。
需要注意的是,使用過多的引數呼叫函式是一個錯誤。
例如:
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 可選引數語法 ([ ]) 來表達可選性。例如:
jsTry/*** @param {string} [somebody] - Somebody's name.*/functionsayHello (somebody ) {if (!somebody ) {somebody = "John Doe";}console .log ("Hello " +somebody );}sayHello ();
可變引數(Var-args)宣告根據 arguments 的使用進行推斷
函式體中引用了 arguments 的函式被隱式視為具有可變引數(即 (...arg: any[]) => any)。使用 JSDoc 可變引數語法指定引數型別。
jsTry/** @param {...number} args */functionsum (/* numbers */) {vartotal = 0;for (vari = 0;i <arguments .length ;i ++) {total +=arguments [i ];}returntotal ;}
未指定的型別引數預設為 any
由於 JavaScript 中沒有指定泛型型別引數的自然語法,未指定的型別引數預設為 any。
在 extends 子句中
例如,React.Component 被定義為具有兩個型別引數 Props 和 State。在 .js 檔案中,沒有合法的方法在 extends 子句中指定這些引數。預設情況下,型別引數將為 any。
jsimport { Component } from "react";class MyComponent extends Component {render() {this.props.b; // Allowed, since this.props is of type any}}
使用 JSDoc @augments 明確指定型別。例如:
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 引用中
JSDoc 中未指定的型別引數預設為 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
在函式呼叫中
對泛型函式的呼叫使用引數來推斷型別引數。有時此過程無法推斷出任何型別,主要是由於缺乏推斷源;在這種情況下,型別引數將預設為 any。例如:
jsvar p = new Promise((resolve, reject) => {reject();});p; // Promise<any>;
要了解 JSDoc 中可用的所有功能,請參閱 參考文件。