由於 Javascript 是動態型別語言,變數型別的寬容使得程式撰寫上有不錯的靈活性,但是當專案變得龐大時,反而會使開發成本提高。
使用
TypeScript
能有效解決動態型別的缺點,但考慮到學習成本與舊專案的重構,可試試 VSCode 中基於 TypeScript 提供對於 JSDoc 支持,實現智能型別檢查。
1. 開啟型別檢查
最簡單的方法就是在
.js
檔案開頭新增
@ts-check
:
// @ts-check
那麼 VSCode 就會根據註解檢查型別。
如果不想每隻 .js
檔案都加上 @ts-check
,可以開啟 VSCode 全域型別檢查:
"js/ts.implicitProjectConfig.checkJs": true
預設的情況下是關閉的:
如果型別檢查為開啟,則可以使用 @ts-nocheck
忽略某隻檔案的類型檢查:
// @ts-nocheck
2. jsconfig.json
除了啟用 VSCode 的設定來開啟型別檢查,也可以在專案根目錄下新增 jsconfig.json
設定檔:
"compilerOptions": {
"checkJs": true
jsconfig.json
會覆蓋 Implicit Project Config: Check JS 設定。
預設會全域檢查所有 .js
檔案,可以使用 exclude
或 include
選項,設定要排除或包含的資料夾:
"compilerOptions": {
"checkJs": true
"exclude": ["node_modules", "dist"],
"include": ["src/**/*"]
Visual Studio Code - jsconfig.json
TypeScript - What is a tsconfig.json
3. 支持的 JSDoc 標籤
TypeScript 只支持了部分 JSDoc 標籤。
當前所支持的標籤如下:
@type
@param
(or @arg
or @argument)
@returns
(or @return)
@typedef
@callback
@template
@class
(or @constructor)
@this
@extends
(or @augments)
@enum
JSDoc Reference
3. 簡單範例
JSDoc 註解格式必須以 /**
為開頭才能被識別(例如 /*
、/***
不會被解析為 JSDoc 註解):
/** */
舉例來說,我們定義一個 count
變數:
let count;
我們可以指定任何型別的值給它,但如果希望它只接受數字型別,可以加上 JSDoc 註解:
/** @type {number} */
let count;
若指定非數字型別的值,VSCode 將會跳出錯誤提示:
可以在註解第一行加上說明:
* 計數變數
* @type {number}
let count;
如果想要忽略類型錯誤可以使用 @ts-ignore
。
JSDoc 標籤
1. @type
@type
可以用來標明變數型別。
基本用法:
/** @type {string} */
let str;
str = 'Hello!';
/** @type {Date} */
let now;
now = new Date();
/** @type {HTMLElement} */
let dom;
dom = document.querySelector('body');
複合型別:
/** @type {string|boolean} */
let x;
x = '123';
x = true;
指定陣列元素的型別:
/** @type {number[]} */
const ns = [];
[].push(1);
也可以寫成 Array.<number>
、Array<number>
:
/** @type {Array.<number>} */
const ns = [];
/** @type {Array<number>} */
const ns2 = [];
物件字面值:
* @type {{ a: string, b: number }}
let obj;
obj = { a: '123', b: 123 };
指定 map-like
和 array-like
的物件:
* @type {Object.<string, number>}
let stringToNumber;
/** @type {Object.<number, object>} */
let arrayLike;
預設就是 any
任意型別:
/** @type {any} */
let x;
*
和 ?
等同 any
:
/** @type {*} - can be 'any' type*/
let y;
/** @type {?} - unknown type*/
let z;
函式型別:
* @type {function(number): number} Closure syntax
let foo;
foo = function (a) {
return a * a;
* @type {(a: number) => number} Typescript syntax
let boo;
boo = function (a) {
return a * a;
2. @param(synonyms: @arg or @argument)
@param
語法和 @type
基本上相同,但用於標明函式參數,所以多了參數名稱。
* The square of a number
* @param {number} number - 輸入數字
* @return {number}
function square(number) {
return number * number;
函式如果有回傳值,則可以使用 @returns
(@return
) 標明。
有屬性的參數,使用物件字面值不易描述屬性:
* @param {{ name: string, age: number }} person
function foo(person) {
console.log(person.name, person.age);
可以使用以下寫法:
* @param {Object} person - 某人
* @param {string} person.name - 某人的名字
* @param {number} person.age - 某人的年齡
function foo(person) {
console.log(person.name, person.age);
使用 ES6 參數解構,使用適當的參數名稱即可:
* @param {Object} person - 某人
* @param {string} person.name - 名字
* @param {number} person.age - 年齡
function foo({ name, age }) {
console.log(name, age);
可選參數表示方式:
* @param {string=} p1 - 可選參數(Closure語法)
* @param {string} [p2] - 可選參數(JSDoc語法)
* @param {string} [p3 = 'test'] - 有預設值的可選參數(JSDoc語法)
function foo(p1, p2, p3 = 'test') {}
3. @typedef
@typedef
可以用來宣告複雜型別,也就是自訂義一個類型,再使用 @type
標記來引用。
描述一個物件型別:
* @typedef {Object} SpecialType - creates a new type named 'SpecialType'
* @property {string} prop1 - a string property
* @property {number} prop2 - a number property
* @prop {number} [prop3] - an optional number property of SpecialType
/** @type {SpecialType} */
let obj;
obj = { prop1: '123', prop2: 123 };
4. @callback
@callback
與 @typedef
相似,但描述的是一個函式:
* @callback Predicate
* @param {string} data
* @returns {boolean}
/** @type {Predicate} */
const foo = function (str) {
return !(str.length % 2);
5. @class(synonyms: @constructor)
@class
可以標明函式為一個建構函式(Constructor)。
* Creates a new Person.
* @class
function Person() {}
const p = new Person();
不過 ES6 有了 class
後,就沒必要使用 @class
了。
class Person {
* Creates a new Person.
constructor() {}
6. @this
@this
可以明確標示 this
關鍵字在這裡指的是什麼。
例如建構函式的方法:
* @class
function Person() {
this.name = '';
* @this {Person} - Person 實體
Person.prototype.setName = function (name) {
this.name = name;
或監聽器處理函式:
* @param {Event} event - 事件物件
* @this {HTMLElement} - 監聽器綁定元素
function clickHandler(event) {
// ...
7. @extends(synonyms: @augments)
如果使用 extends
關鍵字來擴展一個現有的類別的時候,可以使用 @extends
標示。
/** 佇列 */
class Queue {}
* 優先佇列
* @extends Queue
class PriorityQueue extends Queue {}
8. @enum
@enum
標籤描述一個靜態屬性值的全部相同的集合,簡單來說就是一個物件內的屬性皆為相同型別,且不允許新增額外屬性。
/** @enum {number} */
const JSDocState = {
BeginningOfLine: 0,
SawAsterisk: 1,
SavingComments: 2,
9. @template
@templete
非 JSDoc 標準,只在 google closure compiler 中有提及,可以用來宣告 泛用型別(Generic Type),是 TypeScript 中的型別。
泛用型別(Generic Type) 目的在於成員之間提供有意義的約束,這些成員可以是類別的實體、類別的方法、函式參數、函式回傳值。
* @template T
* @param {T} x
* @return {T}
function foo(x) {
return x;
關於 泛用型別(Generic Type) 我自己也不是很了解,有空在補充。
註解相關 VSCode 套件
koroFileHeader,在 VSCode 中用於生成檔案頭部註解和函式註解的套件。
文件頭部新增註解:Ctrl
+ Alt
+ t
光標處添加函式註解:Ctrl
+ Alt
+ t
Todo Tree,特殊註解高光亮。
// 配置
"todo-tree.general.tags": ["TODO:", "FIXME:"],
"todo-tree.highlights.defaultHighlight": {
"gutterIcon": true,
"foreground": "#fff",
"type": "text",
"opacity": 50
"todo-tree.highlights.customHighlight": {
"TODO:": {
"background": "#ffbd2a",
"iconColour": "#ffbd2a"
"FIXME:": {
"background": "#f06292",
"iconColour": "#f06292",
"icon": "flame"
可不可以不要寫糙 code系列 第 8 篇 - 不必要的註解
编写高质量可维护的代码:一目了然的注释
導入 TypeScript 應考慮之效益與成本
為你的 JavaScript 項目添加智能提示和類型檢查
[Vuex]在 Vscode 中不使用 Typescript 也能讓 Vuex 讀懂變數的類型
JSDoc中文文档(@use JSDoc)
基于TypeScript的JSDoc注释
@ts-check 立即上手,JSDoc 添加类型
如何使用 JSDoc 保證你的 Javascript 型別安全性
TS in JS 实践指北
[Web翻译]从JavaScript生成TypeScript定义文件
[译] 讨论 JS ⚡:文档
使用 JSDoc 标注类型
利用 VSCode、JSDoc、d.ts 让你的代码更健壮
ES6写JSDoc的一些经验和实例
震驚!JavaScript 竟然可以類型推斷!
“無 Typescript” 編程
JSDoc support in JavaScript
JSDoc支持_TypeScript筆記19
Typescript 初心者手札系列 第 3 篇 - TypeScript 編譯設定 - tsconfig.json
vscode中的 jsconfig.json
為什麼世界需要Typescript系列 第 7 篇 - 泛型 - 07
你不知道的 TypeScript 泛型(萬字長文,建議收藏)
TypeScript系列(四)泛型