Chú thích JavaScript cho Trình biên dịch Closure

Lưu ý: Trang này đã cũ. Danh sách đầy đủ được duy trì tại https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler

Tổng quan

Trình biên dịch Closure có thể sử dụng thông tin về kiểu dữ liệu của các biến JavaScript để cung cấp thông tin tối ưu hoá và cảnh báo nâng cao. Tuy nhiên, JavaScript không có cách nào để khai báo các loại.

Vì JavaScript không có cú pháp để khai báo kiểu của một biến, nên bạn phải sử dụng chú thích trong mã để chỉ định kiểu dữ liệu.

Ngôn ngữ kiểu của Trình biên dịch Closure bắt nguồn từ các chú giải mà công cụ tạo tài liệu JSDoc sử dụng, mặc dù ngôn ngữ này đã khác biệt kể từ đó. Giờ đây, tệp này bao gồm một số chú thích mà JSDoc không hỗ trợ và ngược lại. Tài liệu này mô tả tập hợp các chú thích và biểu thức kiểu mà Trình biên dịch Closure hiểu được.

  1. Thẻ JSDoc
  2. Biểu thức kiểu dữ liệu
  3. Các loại chung

Thẻ JSDoc

Trình biên dịch Closure tìm kiếm thông tin về kiểu trong thẻ JSDoc. Sử dụng các thẻ JSDoc được mô tả trong bảng tham chiếu bên dưới để giúp trình biên dịch tối ưu hoá mã của bạn và kiểm tra mã để tìm các lỗi có thể xảy ra về kiểu và các lỗi khác.

Bảng này chỉ bao gồm những thẻ ảnh hưởng đến hành vi của Closure Compiler. Để biết thông tin về các thẻ JSDoc khác, hãy xem tài liệu về Bộ công cụ JSDoc.

Thẻ Mô tả
@abstract

Đánh dấu một phương thức là trừu tượng. Tương tự như việc đặt một phương thức thành goog.abstractMethod, trình biên dịch có thể cắt tỉa các phương thức được chú thích bằng @abstract để giảm kích thước mã.

Trình biên dịch sẽ tạo cảnh báo nếu một phương thức được đánh dấu bằng @abstract có một triển khai không trống.

Ví dụ:
/** @abstract */
foo.MyClass.prototype.abstractMethod = function() {};
@const

Đánh dấu một biến là chỉ đọc. Trình biên dịch có thể nội tuyến các biến @const, giúp tối ưu hoá mã JavaScript.

Bạn không bắt buộc phải khai báo loại.

Trình biên dịch sẽ tạo cảnh báo nếu một biến được đánh dấu bằng @const được gán giá trị nhiều lần. Nếu biến là một đối tượng, hãy lưu ý rằng trình biên dịch không cấm các thay đổi đối với thuộc tính của đối tượng.

Ví dụ:
/** @const */ var MY_BEER = 'stout';

/**
 * My namespace's favorite kind of beer.
 * @const {string}
 */
mynamespace.MY_BEER = 'stout';

/** @const */ MyClass.MY_BEER = 'stout';
@constructor

Đánh dấu một hàm là hàm khởi tạo. Trình biên dịch yêu cầu chú giải @constructor cho mọi hàm được dùng với từ khoá new

Ví dụ:

/**
 * A rectangle.
 * @constructor
 */
function GM_Rect() {
  ...
}
@define Cho biết một hằng số mà trình biên dịch có thể ghi đè tại thời gian biên dịch. Với ví dụ ở bên trái, bạn có thể truyền cờ --define='ENABLE_DEBUG=false' đến trình biên dịch để thay đổi giá trị của ENABLE_DEBUG thành false. Loại hằng số được xác định có thể là số, chuỗi hoặc boolean. Bạn chỉ được phép xác định trong phạm vi chung.

Ví dụ:

/** @define {boolean} */
var ENABLE_DEBUG = true;

/** @define {boolean} */
goog.userAgent.ASSUME_IE = false;
@deprecated

Đánh dấu một hàm, phương thức hoặc thuộc tính để việc sử dụng hàm, phương thức hoặc thuộc tính đó sẽ tạo ra cảnh báo của trình biên dịch cho biết rằng bạn không nên sử dụng hàm, phương thức hoặc thuộc tính đó nữa.

Ví dụ:

/**
 * Determines whether a node is a field.
 * @return {boolean} True if the contents of
 *     the element are editable, but the element
 *     itself is not.
 * @deprecated Use isField().
 */
BN_EditUtil.isTopEditableField = function(node) {
  ...
};
@dict

@dict được dùng để tạo các đối tượng có số lượng thuộc tính thay đổi. Khi một hàm dựng (Foo trong ví dụ) được chú thích bằng @dict, bạn chỉ có thể sử dụng ký hiệu dấu ngoặc vuông để truy cập vào các thuộc tính của đối tượng Foo. Bạn cũng có thể sử dụng chú thích trực tiếp trên các chữ đối tượng.

Ví dụ:

/**
 * @constructor
 * @dict
 */
function Foo() {}
var obj1 = new Foo();
obj1['x'] = 123;
obj1.x = 234;  // warning

var obj2 = /** @dict */ { 'x': 321 };
obj2.x = 123;  // warning
@enum

Chỉ định loại của một enum. Enum là một đối tượng có các thuộc tính tạo thành một tập hợp các hằng số có liên quan. Thẻ @enum phải đứng sau một biểu thức loại.

Nhãn loại của một enum áp dụng cho từng thuộc tính của enum. Ví dụ: nếu một enum có loại number, thì mỗi thuộc tính được liệt kê của enum đó phải là một số. Nếu bạn bỏ qua loại của một enum, thì number sẽ được giả định.

Ví dụ:

/**
 * Enum for tri-state values.
 * @enum {number}
 */
project.TriState = {
  TRUE: 1,
  FALSE: -1,
  MAYBE: 0
};
@export

Với đoạn mã này

/** @export */
foo.MyPublicClass.prototype.myPublicMethod = function() {
  // ...
};

khi trình biên dịch chạy với cờ --generate_exports, trình biên dịch sẽ tạo mã:

goog.exportProperty(foo.MyPublicClass.prototype, 'myPublicMethod',
  foo.MyPublicClass.prototype.myPublicMethod);

thao tác này sẽ xuất các biểu tượng sang mã chưa biên dịch. Bạn có thể viết /** @export {SomeType} */ thay cho

/**
 * @export
 * @type {SomeType}
 */

Mã sử dụng chú thích @export phải

  1. bao gồm closure/base.js hoặc
  2. xác định cả goog.exportSymbolgoog.exportProperty bằng cùng một chữ ký phương thức trong cơ sở mã riêng của chúng.
@extends

Đánh dấu một lớp hoặc giao diện là kế thừa từ một lớp khác. Một lớp được đánh dấu bằng @extends cũng phải được đánh dấu bằng @constructor hoặc @interface.

Lưu ý: @extends không khiến một lớp kế thừa từ một lớp khác. Chú thích này chỉ cho trình biên dịch biết rằng nó có thể coi một lớp là lớp con của một lớp khác trong quá trình kiểm tra kiểu.

Để xem ví dụ về cách triển khai tính kế thừa, hãy xem hàm Thư viện Closure goog.inherits().

Ví dụ:

/**
 * Immutable empty node list.
 * @constructor
 * @extends {goog.ds.BasicNodeList}
 */
goog.ds.EmptyNodeList = function() {
  ...
};
@final

Cho biết rằng bạn không được phép mở rộng lớp này. Đối với các phương thức, cho biết rằng không có lớp con nào được phép ghi đè phương thức đó.

Ví dụ:

/**
 * A class that cannot be extended.
 * @final
 * @constructor
 */
sloth.MyFinalClass = function() { ... }

/**
 * A method that cannot be overridden.
 * @final
 */
sloth.MyFinalClass.prototype.method = function() { ... };
@implements

Được dùng với @constructor để cho biết một lớp triển khai một giao diện.

Trình biên dịch sẽ đưa ra cảnh báo nếu bạn gắn thẻ một hàm khởi tạo bằng @implements rồi không triển khai tất cả các phương thức và thuộc tính do giao diện xác định.

Ví dụ:

/**
 * A shape.
 * @interface
 */
function Shape() {};
Shape.prototype.draw = function() {};

/**
 * @constructor
 * @implements {Shape}
 */
function Square() {};
Square.prototype.draw = function() {
  ...
};
@implicitCast

Chú thích này chỉ có thể xuất hiện trong các khai báo thuộc tính bên ngoài. Thuộc tính có một loại được khai báo, nhưng bạn có thể chỉ định bất kỳ loại nào cho thuộc tính đó mà không có cảnh báo. Khi truy cập vào thuộc tính, bạn sẽ nhận lại được một giá trị thuộc loại đã khai báo. Ví dụ: element.innerHTML có thể được chỉ định bất kỳ loại nào, nhưng sẽ luôn trả về một chuỗi.

/**
 * @type {string}
 * @implicitCast
 */
Element.prototype.innerHTML;
@inheritDoc

Cho biết rằng một phương thức hoặc thuộc tính của lớp con cố tình ẩn một phương thức hoặc thuộc tính của lớp cấp cao và có tài liệu hoàn toàn giống nhau. Xin lưu ý rằng thẻ @inheritDoc ngụ ý thẻ @override.

Ví dụ:

/** @inheritDoc */
project.SubClass.prototype.toString = function() {
  ...
};
@interface

Đánh dấu một hàm là giao diện. Giao diện chỉ định các thành viên bắt buộc của một loại. Mọi lớp triển khai một giao diện đều phải triển khai tất cả các phương thức và thuộc tính được xác định trên nguyên mẫu của giao diện. Hãy xem @implements.

Trình biên dịch xác minh rằng các giao diện không được khởi tạo. Nếu từ khoá new được dùng với một hàm giao diện, trình biên dịch sẽ đưa ra cảnh báo.

Ví dụ:

/**
 * A shape.
 * @interface
 */
function Shape() {};
Shape.prototype.draw = function() {};

/**
 * A polygon.
 * @interface
 * @extends {Shape}
 */
function Polygon() {};
Polygon.prototype.getSides = function() {};
@lends

Cho biết rằng các khoá của một đối tượng chữ cần được coi là thuộc tính của một số đối tượng khác. Chú thích này chỉ xuất hiện trên các giá trị cố định của đối tượng.

Lưu ý rằng tên trong dấu ngoặc nhọn không phải là tên loại như trong các chú thích khác. Đây là tên đối tượng. Nó đặt tên cho đối tượng mà các thuộc tính được cho mượn. Ví dụ: @type {Foo} có nghĩa là "một thực thể của Foo", nhưng @lends {Foo} có nghĩa là "hàm khởi tạo Foo".

Tài liệu về Bộ công cụ JSDoc có thêm thông tin về chú thích này.

Ví dụ:

goog.object.extend(
    Button.prototype,
    /** @lends {Button.prototype} */ ({
      isButton: function() { return true; }
    }));
@license hoặc @preserve

Yêu cầu trình biên dịch chèn nhận xét được liên kết trước mã đã biên dịch cho tệp được đánh dấu. Chú thích này cho phép các thông báo quan trọng (chẳng hạn như giấy phép pháp lý hoặc văn bản bản quyền) tồn tại mà không thay đổi trong quá trình biên dịch. Dấu ngắt dòng được giữ nguyên.

Ví dụ:

/**
 * @preserve Copyright 2009 SomeThirdParty.
 * Here is the full license text and copyright
 * notice for this file. Note that the notice can span several
 * lines and is only terminated by the closing star and slash:
 */
@nocollapse

Biểu thị một thuộc tính mà trình biên dịch không được thu gọn thành một biến. Mục đích chính của @nocollapse là cho phép xuất các thuộc tính có thể thay đổi. Xin lưu ý rằng trình biên dịch vẫn có thể đổi tên các thuộc tính không được thu gọn. Nếu bạn chú thích một thuộc tính là đối tượng bằng @nocollapse, thì tất cả các thuộc tính của đối tượng đó cũng sẽ vẫn không bị thu gọn.

Ví dụ:

/**
 * A namespace.
 * @const
 */
var foo = {};

/**
 * @nocollapse
 */
foo.bar = 42;

window['foobar'] = foo.bar;
@nosideeffects

Cho biết rằng lệnh gọi đến hàm bên ngoài đã khai báo không có tác dụng phụ. Chú giải này cho phép trình biên dịch xoá các lệnh gọi đến hàm nếu giá trị trả về không được dùng. Bạn chỉ được phép sử dụng chú thích trong extern files.

Ví dụ:

/** @nosideeffects */
function noSideEffectsFn1() {}

/** @nosideeffects */
var noSideEffectsFn2 = function() {};

/** @nosideeffects */
a.prototype.noSideEffectsFn3 = function() {};
@override

Cho biết rằng một phương thức hoặc thuộc tính của lớp con cố tình ẩn một phương thức hoặc thuộc tính của lớp cấp cao. Nếu không có chú thích nào khác, phương thức hoặc thuộc tính sẽ tự động kế thừa chú thích từ lớp cấp cao.

Ví dụ:

/**
 * @return {string} Human-readable representation of
 *     project.SubClass.
 * @override
 */
project.SubClass.prototype.toString = function() {
  ...
};
@package

Đánh dấu một thành viên hoặc thuộc tính là riêng tư theo gói. Chỉ mã trong cùng một thư mục mới có thể truy cập vào các tên được đánh dấu @package. Cụ thể, mã trong thư mục mẹ và thư mục con không thể truy cập vào các tên được đánh dấu @package.

Các hàm khởi tạo công khai có thể có các thuộc tính @package để hạn chế các phương thức mà người gọi bên ngoài thư mục có thể sử dụng. Mặt khác, các hàm khởi tạo @package có thể có các thuộc tính công khai để ngăn người gọi bên ngoài thư mục trực tiếp khởi tạo một kiểu.

Ví dụ:

/**
 * Returns the window object the foreign document resides in.
 *
 * @return {Object} The window object of the peer.
 * @package
 */
goog.net.xpc.CrossPageChannel.prototype.getPeerWindowObject = function() {
  // ...
};
@param

Được dùng với các định nghĩa về phương thức, hàm và hàm khởi tạo để chỉ định các loại đối số của hàm. Thẻ @param phải theo cùng thứ tự với các tham số trong định nghĩa hàm.

Thẻ @param phải được theo sau bởi một biểu thức loại.

Ngoài ra, bạn có thể chú giải các loại tham số nội tuyến (xem hàm foo trong ví dụ).

Ví dụ:

/**
 * Queries a Baz for items.
 * @param {number} groupNum Subgroup id to query.
 * @param {string|number|null} term An itemName,
 *     or itemId, or null to search everything.
 */
goog.Baz.prototype.query = function(groupNum, term) {
  ...
};

function foo(/** number */ a, /** number */ b) {
  return a - b + 1;
}
Đối với các tham số là một mẫu phân tách, bạn có thể sử dụng bất kỳ tên nào là một giá trị nhận dạng JS hợp lệ, sau chú thích loại.
/**
 * @param {{name: string, age: number}} person
 */
function logPerson({name, age}) {
  console.log(`${name} is ${age} years old`);
}
@private

Đánh dấu một thành viên là riêng tư. Chỉ mã trong cùng một tệp mới có thể truy cập vào các biến và hàm chung được đánh dấu @private. Chỉ mã trong cùng một tệp và các thành viên tĩnh và thành viên phiên bản của chúng mới có thể tạo thực thể cho các hàm khởi tạo được đánh dấu @private.

Bạn cũng có thể truy cập vào các thuộc tính tĩnh công khai của hàm khởi tạo được đánh dấu @private ở bất cứ đâu và toán tử instanceof luôn có thể truy cập vào các thành phần @private.

Ví dụ:

/**
 * Handlers that are listening to this logger.
 * @private {Array<Function>}
 */
this.handlers_ = [];
@protected

Cho biết rằng một thành phần hoặc thuộc tính được bảo vệ.

Một tài sản được đánh dấu @protected có thể truy cập vào:

  • tất cả mã trong cùng một tệp
  • các phương thức tĩnh và phương thức thực thể của mọi lớp con của lớp mà thuộc tính được xác định.

Ví dụ:

/**
 * Sets the component's root element to the given element.
 * Considered protected and final.
 * @param {Element} element Root element for the component.
 * @protected
 */
goog.ui.Component.prototype.setElementInternal = function(element) {
  // ...
};
@record

Đánh dấu một hàm là giao diện cấu trúc. Giao diện cấu trúc tương tự như @interface danh nghĩa, nhưng cho phép triển khai ngầm. Điều này có nghĩa là bất kỳ lớp nào bao gồm các phương thức và thuộc tính được xác định trên nguyên mẫu của giao diện cấu trúc đều triển khai giao diện cấu trúc, cho dù lớp đó có sử dụng thẻ @implements hay không. Các loại bản ghi và chữ số đối tượng cũng ngầm triển khai một giao diện cấu trúc nếu chúng chứa các thuộc tính bắt buộc.

Ví dụ:

/**
 * Anything with a draw() method.
 * @record
 */
function Drawable() {};
Drawable.prototype.draw = function() {};

/**
 * A polygon.
 * @param {!Drawable} x
 */
function render(x) { x.draw(); };

var o = { draw() { /* ... */ } };
render(o);
@return

Chỉ định kiểu trả về của các định nghĩa về phương thức và hàm. Thẻ @return phải được theo sau bởi một biểu thức loại.

Ngoài ra, bạn có thể chú thích kiểu dữ liệu trả về nội tuyến (xem hàm foo trong ví dụ).

Nếu một hàm không có trong externs không có giá trị trả về, bạn có thể bỏ qua thẻ @return và trình biên dịch sẽ giả định rằng hàm trả về undefined.

Ví dụ:

/**
 * Returns the ID of the last item.
 * @return {string} The hex ID.
 */
goog.Baz.prototype.getLastId = function() {
  ...
  return id;
};

function /** number */ foo(x) { return x - 1; }
@struct

@struct được dùng để tạo các đối tượng có số lượng thuộc tính cố định. Khi một hàm dựng (Foo trong ví dụ) được chú thích bằng @struct, bạn chỉ có thể sử dụng ký hiệu dấu chấm để truy cập vào các thuộc tính của đối tượng Foo, chứ không thể sử dụng ký hiệu dấu ngoặc vuông. Ngoài ra, bạn không thể thêm một thuộc tính vào một thực thể Foo sau khi thực thể đó được tạo. Bạn cũng có thể sử dụng chú thích trực tiếp trên các chữ đối tượng.

Ví dụ:

/**
 * @constructor
 * @struct
 */
function Foo(x) {
  this.x = x;
}
var obj1 = new Foo(123);
var someVar = obj1.x;  // OK
obj1.x = "qwerty";  // OK
obj1['x'] = "asdf";  // warning
obj1.y = 5;  // warning

var obj2 = /** @struct */ { x: 321 };
obj2['x'] = 123;  // warning
@template

Xem phần Loại chung.

Ví dụ:

/**
 * @param {T} t
 * @constructor
 * @template T
 */
Container = function(t) { ... };
@this

Chỉ định loại đối tượng mà từ khoá this đề cập đến trong một hàm. Thẻ @this phải được theo sau bởi một biểu thức loại.

Để ngăn trình biên dịch đưa ra cảnh báo, bạn phải sử dụng chú thích @this bất cứ khi nào this xuất hiện trong một hàm không phải là phương thức nguyên mẫu cũng không phải là hàm được đánh dấu là @constructor.

Ví dụ:

chat.RosterWidget.extern('getRosterElement',
    /**
     * Returns the roster widget element.
     * @this {Widget}
     * @return {Element}
     */
    function() {
      return this.getComponent().getElement();
    });
@throws

Dùng để ghi lại các ngoại lệ do một hàm gửi. Trình kiểm tra loại hiện không sử dụng thông tin này. Nó chỉ được dùng để tìm hiểu xem một hàm được khai báo trong tệp externs có tác dụng phụ hay không.

Ví dụ:

/**
 * @throws {DOMException}
 */
DOMApplicationCache.prototype.swapCache = function() { ... };
@type

Xác định loại biến, thuộc tính hoặc biểu thức. Thẻ @type phải đi kèm với một biểu thức loại.

Khi khai báo một biến hoặc tham số hàm, bạn có thể viết chú thích loại nội tuyến mà không cần {}@type, như trong ví dụ thứ hai. Bạn chỉ có thể thực hiện lối tắt này khi khai báo một biến hoặc tham số hàm. Nếu muốn điều chỉnh loại sau này, bạn sẽ cần một truyền kiểu.

Ví dụ:

/**
 * The message hex ID.
 * @type {string}
 */
var hexId = hexId;
var /** string */ name = 'Jamie';
function useSomething(/** (string|number|!Object) */ something) {
...
}
@typedef

Khai báo một biệt hiệu cho một loại phức tạp hơn. Hiện tại, bạn chỉ có thể xác định typedef ở cấp cao nhất, chứ không thể xác định bên trong các hàm. Chúng tôi đã khắc phục hạn chế này trong suy luận kiểu mới.

Ví dụ:

/** @typedef {(string|number)} */
goog.NumberLike;

/** @param {goog.NumberLike} x A number or a string. */
goog.readNumber = function(x) {
  ...
}
@unrestricted

Cho biết rằng một lớp không phải là kiểu @struct cũng không phải là kiểu @dict. Đây là giá trị mặc định nên bạn thường không cần viết một cách rõ ràng, trừ phi bạn đang sử dụng từ khoá class. Cả hai từ khoá này đều tạo ra các lớp @struct theo mặc định.

Ví dụ:

/**
 * @constructor
 * @unrestricted
 */
function Foo(x) {
  this.x = x;
}
var obj1 = new Foo(123);
var someVar = obj1.x;  // OK
obj1.x = "qwerty";  // OK
obj1['x'] = "asdf";  // OK
obj1.y = 5;  // OK

Biểu thức loại

Bạn có thể chỉ định loại dữ liệu của bất kỳ biến, thuộc tính, biểu thức hoặc tham số hàm nào bằng một biểu thức loại. Biểu thức loại bao gồm dấu ngoặc nhọn ("{ }") chứa một số tổ hợp của các toán tử loại được mô tả bên dưới.

Sử dụng biểu thức loại có thẻ @param để khai báo loại tham số hàm. Sử dụng biểu thức kiểu với thẻ @type để khai báo kiểu của một biến, thuộc tính hoặc biểu thức.

Bạn chỉ định càng nhiều loại trong mã, trình biên dịch có thể thực hiện càng nhiều hoạt động tối ưu hoá và phát hiện càng nhiều lỗi.

Trình biên dịch sử dụng các chú thích này để kiểm tra kiểu chương trình của bạn. Xin lưu ý rằng Closure Compiler không hứa hẹn rằng trình biên dịch này sẽ có thể tìm ra loại của mọi biểu thức trong chương trình của bạn. Trình phân tích cố gắng hết sức bằng cách xem xét cách sử dụng các biến và chú thích kiểu được đính kèm vào các khai báo của biến. Sau đó, trình biên dịch sẽ sử dụng một số thuật toán suy luận kiểu dữ liệu để tìm ra kiểu của nhiều biểu thức nhất có thể. Một số thuật toán trong số này khá đơn giản ("nếu x là một số và chúng ta thấy y = x;, thì y là một số"). Một số quy tắc mang tính gián tiếp hơn ("nếu tham số đầu tiên của f's được ghi lại dưới dạng một lệnh gọi lại phải lấy một số và chúng ta thấy f(function(x) { /** ... */ });, thì x phải là một số").

Tên nhà vận hành Ví dụ về cú pháp Mô tả
Nhập tên {boolean}
{Window}
{goog.ui.Menu}
Chỉ định tên của một loại.
Nhập ứng dụng {Array<string>}
Một mảng chuỗi.

{Object<string, number>}
Một đối tượng trong đó các khoá là chuỗi và các giá trị là số.

Tham số hoá một loại bằng một tập hợp đối số loại. Tương tự như các thành phần chung của Java.
Type Union {(number|boolean)}
Một số hoặc một giá trị boolean.

Lưu ý dấu ngoặc đơn (bắt buộc).
Cho biết rằng một giá trị có thể có loại A HOẶC loại B.
Loại bản ghi {{myNum: number, myObject}}
Một loại ẩn danh có cả thuộc tính có tên là myNum có giá trị thuộc loại number và thuộc tính có tên là myObject có giá trị thuộc bất kỳ loại nào.

Cho biết rằng giá trị có các thành phần được chỉ định với các giá trị thuộc các loại được chỉ định.

Dấu ngoặc nhọn là một phần của cú pháp kiểu. Ví dụ: để biểu thị một Array gồm các đối tượng có thuộc tính length, bạn có thể viết:
Array<{length}>. Trong ví dụ ở bên trái, các dấu ngoặc nhọn bên ngoài cho biết đây là một biểu thức kiểu và các dấu ngoặc nhọn bên trong cho biết đây là một kiểu bản ghi.

Loại có thể rỗng {?number}
Một số hoặc null.

Cho biết rằng một giá trị là loại A hoặc null.

Theo mặc định, tất cả các loại đối tượng đều có thể rỗng cho dù chúng có được khai báo bằng toán tử Nullable hay không. Một kiểu đối tượng được xác định là mọi thứ ngoại trừ hàm, chuỗi, số hoặc boolean. Để tạo một kiểu đối tượng không thể rỗng, hãy dùng toán tử Không thể rỗng.

Loại không thể rỗng {!Object}
Một đối tượng, nhưng không bao giờ có giá trị null.

Cho biết rằng một giá trị thuộc loại A và không có giá trị rỗng.

Theo mặc định, các hàm và tất cả các loại giá trị (boolean, số và chuỗi) đều không thể rỗng, cho dù chúng có được khai báo bằng toán tử Không thể rỗng hay không. Để tạo một giá trị hoặc loại hàm có thể có giá trị rỗng, hãy dùng toán tử Nullable.

Loại hàm {function(string, boolean)}
Một hàm nhận 2 tham số (một chuỗi và một giá trị boolean) và có giá trị trả về không xác định.
Chỉ định một hàm và các loại tham số của hàm.
Loại dữ liệu trả về của hàm {function(): number}
Một hàm không nhận tham số và trả về một số.
Chỉ định loại giá trị trả về của một hàm.
Loại hàm this {function(this:goog.ui.Menu, string)}
Một hàm nhận một tham số (một chuỗi) và thực thi trong ngữ cảnh của goog.ui.Menu.
Chỉ định loại giá trị của this trong hàm.
Loại hàm new {function(new:goog.ui.Menu, string)}
Một hàm nhận một tham số (một chuỗi) và tạo một phiên bản mới của goog.ui.Menu khi được gọi bằng từ khoá "new".
Chỉ định loại được tạo của một hàm khởi tạo.
Tham số biến {function(string, ...number): number}
Một hàm nhận một tham số (một chuỗi), sau đó là một số lượng tham số có thể thay đổi phải là số.
Cho biết rằng một loại hàm nhận một số lượng tham số có thể thay đổi và chỉ định một loại cho các tham số có thể thay đổi.
Tham số biến (trong chú giải @param) @param {...number} var_args
Số lượng tham số có thể thay đổi cho một hàm được chú thích.
Cho biết hàm được chú thích chấp nhận một số lượng biến của các tham số và chỉ định một loại cho các tham số biến.
Tham số không bắt buộc trong chú giải @param @param {number=} opt_argument
Một tham số không bắt buộc thuộc loại number.

Cho biết rằng đối số được mô tả bằng chú thích @param là không bắt buộc. Một lệnh gọi hàm có thể bỏ qua một đối số không bắt buộc. Một tham số không bắt buộc không thể đứng trước một tham số không bắt buộc trong danh sách tham số.

Nếu một lệnh gọi phương thức bỏ qua một tham số không bắt buộc, thì đối số đó sẽ có giá trị là undefined. Do đó, nếu phương thức lưu trữ giá trị của tham số trong một thuộc tính lớp, thì khai báo kiểu của thuộc tính đó phải bao gồm một giá trị có thể có của undefined, như trong ví dụ sau:

/**
 * Some class, initialized with an optional value.
 * @param {Object=} opt_value Some value (optional).
 * @constructor
 */
function MyClass(opt_value) {
  /**
   * Some value.
   * @type {Object|undefined}
   */
  this.myValue = opt_value;
}
Đối số không bắt buộc trong một kiểu hàm {function(?string=, number=)}
Một hàm nhận một chuỗi có giá trị rỗng và không bắt buộc và một số không bắt buộc làm đối số.
Cho biết rằng một đối số trong kiểu hàm là không bắt buộc. Bạn có thể bỏ qua đối số không bắt buộc trong lệnh gọi hàm. Đối số không bắt buộc không thể đứng trước đối số không bắt buộc trong danh sách đối số.
Loại ALL {*} Cho biết rằng biến có thể nhận bất kỳ loại nào.
Loại UNKNOWN {?} Cho biết rằng biến có thể nhận bất kỳ kiểu nào và trình biên dịch không nên kiểm tra kiểu cho bất kỳ cách sử dụng nào của biến đó.

Chuyển đổi loại

Để truyền một giá trị sang một loại cụ thể, hãy sử dụng cú pháp sau

/** @type {!MyType} */ (valueExpression)
Bạn luôn phải đặt biểu thức trong dấu ngoặc đơn.

Kiểu chung

Tương tự như Java, Closure Compiler hỗ trợ các loại, hàm và phương thức chung. Các kiểu chung hoạt động trên các đối tượng thuộc nhiều loại khác nhau trong khi vẫn duy trì tính an toàn của kiểu tại thời gian biên dịch.

Bạn có thể sử dụng kiểu chung để triển khai các tập hợp chung chứa các tham chiếu đến các đối tượng thuộc một loại cụ thể và các thuật toán chung hoạt động trên các đối tượng thuộc một loại cụ thể.

Khai báo kiểu chung

Bạn có thể tạo một loại chung bằng cách thêm chú thích @template vào hàm khởi tạo của loại (đối với các lớp) hoặc khai báo giao diện (đối với các giao diện). Ví dụ:

/**
 * @constructor
 * @template T
 */
Foo = function() { ... };

Chú thích @template T cho biết Foo là một kiểu chung có một kiểu mẫu, T. Bạn có thể dùng loại mẫu T làm loại trong phạm vi định nghĩa của Foo. Ví dụ:

/** @return {T} */
Foo.prototype.get = function() { ... };

/** @param {T} t */
Foo.prototype.set = function(t) { ... };

Phương thức get sẽ trả về một đối tượng thuộc loại T và phương thức set sẽ chỉ chấp nhận các đối tượng thuộc loại T.

Tạo thực thể cho một kiểu chung

Sử dụng lại ví dụ ở trên, bạn có thể tạo một phiên bản được tạo bằng mẫu của Foo theo một số cách:

/** @type {!Foo<string>} */ var foo = new Foo();
var foo = /** @type {!Foo<string>} */ (new Foo());

Cả hai câu lệnh hàm khởi tạo ở trên đều tạo một thực thể Foo có kiểu mẫu Tstring. Trình biên dịch sẽ thực thi các lệnh gọi đến phương thức của foo và quyền truy cập vào các thuộc tính của foo, tuân theo loại được tạo mẫu. Ví dụ:

foo.set("hello");  // OK.
foo.set(3);        // Error - expected a string, found a number.
var x = foo.get(); // x is a string.

Các thực thể cũng có thể được nhập một cách ngầm định theo các đối số của hàm khởi tạo. Hãy cân nhắc một kiểu chung khác, Bar:

/**
 * @param {T} t
 * @constructor
 * @template T
 */
Bar = function(t) { ... };
var bar = new Bar("hello"); // bar is a Bar<string>

Loại đối số cho hàm khởi tạo Bar được suy luận là string và do đó, phiên bản bar đã tạo được suy luận là Bar<string>.

Nhiều loại mẫu

Một thành phần chung có thể có số lượng loại mẫu bất kỳ. Lớp bản đồ sau đây có 2 loại mẫu:

/**
 * @constructor
 * @template Key, Val
 */
MyMap = function() { ... };

Bạn phải chỉ định tất cả các loại mẫu cho một loại chung trong cùng một chú thích @template dưới dạng một danh sách được phân tách bằng dấu phẩy. Thứ tự của tên loại mẫu là rất quan trọng, vì chú thích loại được tạo mẫu sẽ sử dụng thứ tự này để ghép các loại mẫu với các giá trị. Ví dụ:

/** @type {MyMap<string, number>} */ var map; // Key = string, Val = number.

Tính bất biến của các kiểu chung

Trình biên dịch Closure Compiler thực thi việc nhập chung bất biến. Điều này có nghĩa là nếu một ngữ cảnh mong đợi một loại Foo<X>, bạn không thể truyền một loại Foo<Y> khi XY là các loại khác nhau, ngay cả khi một loại là loại con của loại kia. Ví dụ:

/**
 * @constructor
 */
X = function() { ... };

/**
 * @extends {X}
 * @constructor
 */
Y = function() { ... };

/** @type {Foo<X>} */ var fooX;
/** @type {Foo<Y>} */ var fooY;

fooX = fooY; // Error
fooY = fooX; // Error

/** @param {Foo<Y>} fooY */
takesFooY = function(fooY) { ... };

takesFooY(fooY); // OK.
takesFooY(fooX); // Error

Tính kế thừa của các kiểu chung

Bạn có thể kế thừa các loại chung và loại mẫu của chúng có thể được cố định hoặc truyền đến loại kế thừa. Sau đây là ví dụ về một loại kế thừa sửa loại mẫu của siêu kiểu:

/**
 * @constructor
 * @template T
 */
A = function() { ... };

/** @param {T} t */
A.prototype.method = function(t) { ... };

/**
 * @constructor
 * @extends {A<string>}
 */
B = function() { ... };

Bằng cách mở rộng A<string>, B sẽ có một phương thức method nhận một tham số thuộc loại string.

Sau đây là ví dụ về một loại kế thừa truyền loại mẫu của siêu kiểu:

/**
 * @constructor
 * @template U
 * @extends {A<U>}
 */
C = function() { ... };

Bằng cách mở rộng A<U>, các phiên bản được tạo bằng mẫu của C sẽ có một phương thức method lấy tham số của loại mẫu U.

Các giao diện có thể được triển khai và mở rộng theo cách tương tự, nhưng một loại duy nhất không thể triển khai cùng một giao diện nhiều lần với các loại mẫu khác nhau. Ví dụ:

/**
 * @interface
 * @template T
 */
Foo = function() {};

/** @return {T} */
Foo.prototype.get = function() {};

/**
 * @constructor
 * @implements {Foo<string>}
 * @implements {Foo<number>}
 */
FooImpl = function() { ... }; // Error - implements the same interface twice

Hàm và phương thức chung

Tương tự như các kiểu chung, bạn có thể tạo các hàm và phương thức chung bằng cách thêm chú giải @template vào định nghĩa của chúng. Ví dụ:

/**
 * @param {T} a
 * @return {T}
 * @template T
 */
identity = function(a) { return a; };

/** @type {string} */ var msg = identity("hello") + identity("world"); // OK
/** @type {number} */ var sum = identity(2) + identity(2); // OK
/** @type {number} */ var sum = identity(2) + identity("2"); // Type mismatch