一.JSDoc とタイプチェック
.js ファイル内では TypeScript タイプ注釈構文はサポートされていません:
// 错误 'types' can only be used in a .ts file.
let x: number;
したがって、.js ファイルに対して、JavaScript 構文と互換性のあるタイプ注釈方式が必要 です。例えば JSDoc:
/** @type {number} */
let x;
// 错误 Type '"string"' is not assignable to type 'number'.
x = 'string';
この特殊な形式(/** で始まる)のコメントを通じてタイプを表現し、それによって JavaScript 構文と互換します。TypeScript タイプシステムはこれらの JSDoc マークを解析して追加のタイプ情報入力を取得し、タイプ推論と結合して .js ファイルのタイプチェックを行います
P.S. .js タイプチェックに関するより多くの情報については、[JavaScript ファイルのチェック_TypeScript ノート 18](/articles/检查 javascript 文件-typescript 笔记 18/) を参照
二.サポート程度
TypeScript は現在(2019/5/12)一部の JSDoc マークのみをサポート しています。具体的には:
-
@type:オブジェクトを記述 -
@param(または@argまたは@argument):関数パラメータを記述 -
@returns(または@return):関数戻り値を記述 -
@typedef:カスタムタイプを記述 -
@callback:コールバック関数を記述 -
@class(または@constructor):該関数はnewキーワードを通じて呼び出すべきであることを示す -
@this:此处thisの指向を記述 -
@extends(または@augments):継承関係を記述 -
@enum:一組の関連属性を記述 -
@property(または@prop):オブジェクト属性を記述
P.S.完全な JSDoc マークリストは Block Tags を参照
特別に、ジェネリックに対して、JSDoc には適切なマークが提供されていません。そのため、追加のマークを拡張しました:
@template:ジェネリックを記述
P.S. @template マークでジェネリックを記述するのは Google Closure Compiler から由来し、より多くの関連討論は Add support for @template JSDoc を参照
三.タイプ注釈構文
TypeScript は JSDoc タイプ注釈と互換し、同時に JSDoc マーク中で TypeScript タイプ注釈構文を使用することもサポートします:
The meaning is usually the same, or a superset, of the meaning of the tag given at usejsdoc.org.
そうです、またスーパーセット です。そのため any タイプには 3 種類の注釈方式があります:
// JSDoc タイプ注釈構文
/** @type {*} - can be 'any' type */
var star = true;
/** @type {?} - unknown type (same as 'any') */
var question = true;
// どちらも TypeScript タイプ注釈構文と同等
/** @type {any} */
var thing = true;
構文方面では、JSDoc は主に Google Closure Compiler タイプ注釈 から借用しており、TypeScript には独自の [タイプ構文](/articles/基本类型-typescript 笔记 2/) があるため、二者にはいくつかの差異が存在します
タイプ宣言
@typedef マークを使用してカスタムタイプを宣言します。例えば:
/**
* @typedef {Object} SpecialType - creates a new type named 'SpecialType'
* @property {string} prop1 - a string property of SpecialType
* @property {number} prop2 - a number property of SpecialType
* @property {number=} prop3 - an optional number property of SpecialType
* @prop {number} [prop4] - an optional number property of SpecialType
* @prop {number} [prop5=42] - an optional number property of SpecialType with default
*/
/** @type {SpecialType} */
var specialTypeObject;
以下の TypeScript コードと同等です:
type SpecialType = {
prop1: string;
prop2: number;
prop3?: number;
prop4?: number;
prop5?: number;
}
let specialTypeObject: SpecialType;
タイプ参照
@type マークを通じてタイプ名を参照します。タイプ名は基本タイプでも、TypeScript 宣言ファイル(d.ts)内に定義されているタイプでも、JSDoc マーク @typedef を通じて定義されたタイプでも構いません
例えば:
// 基本タイプ
/**
* @type {string}
*/
var s;
/** @type {number[]} */
var ns;
/** @type {Array.<number>} */
var nds;
/** @type {Array<number>} */
var nas;
/** @type {Function} */
var fn7;
/** @type {function} */
var fn6;
// 外部宣言ファイル中に定義されたタイプ
/** @type {Window} */
var win;
/** @type {PromiseLike<string>} */
var promisedString;
/** @type {HTMLElement} */
var myElement = document.querySelector('#root');
element.dataset.myData = '';
// JSDoc @typedef で定義されたタイプ
/** @typedef {(data: string, index?: number) => boolean} Predicate */
/** @type Predicate */
var p;
p('True or not ?');
オブジェクトタイプもオブジェクトリテラルを通じて記述し、インデックス署名も同様に適用されます:
/** @type {{ a: string, b: number }} */
var obj;
obj.a.toLowerCase();
/**
* 文字列インデックス署名
* @type {Object.<string, number>}
*/
var stringToNumber;
// 同等
/** @type {{ [x: string]: number; }} */
var stringToNumber;
// 数値インデックス署名
/** @type {Object.<number, object>} */
var arrayLike;
// 同等
/** @type {{ [x: number]: any; }} */
var arrayLike;
関数タイプにも 2 種類の構文が選択可能です:
/** @type {function(string, boolean): number} Closure syntax */
var sbn;
/** @type {(s: string, b: boolean) => number} Typescript syntax */
var sbn2;
前者は形式パラメータ名を省略でき、後者は function キーワードを省略でき、意味は同じです
同様にタイプ組合もサポートします:
// ユニオンタイプ(JSDoc タイプ構文)
/**
* @type {(string | boolean)}
*/
var sb;
// ユニオンタイプ(TypeScript タイプ構文)
/**
* @type {string | boolean}
*/
var sb;
二者は同等で、構文にわずかな差異があるだけです
クロスファイルタイプ参照
特別に、import を通じて_他のファイル中に定義されたタイプを参照_ できます:
// a.js
/**
* @typedef Pet
* @property name {string}
*/
module.exports = {/* ... */};
// index.js
// 1. タイプを参照
/**
* @param p { import("./a").Pet }
*/
function walk(p) {
console.log(`Walking ${p.name}...`);
}
// 1. タイプを参照し、同時にエイリアスを付ける
/**
* @typedef { import("./a").Pet } Pet
*/
/**
* @type {Pet}
*/
var myPet;
myPet.name;
// 3. 推論されたタイプを参照
/**
* @type {typeof import("./a").x }
*/
var x = require("./a").x;
注意、この構文は TypeScript 特有のものです(JSDoc はサポートしていません)。JSDoc 中では ES Module 導入構文を採用します:
// a.js
/**
* @typedef State
* @property {Array} layers
* @property {object} product
*/
// index.js
import * as A from './a';
/** @param {A.State} state */
const f = state => ({
product: state.product,
layers: state.layers,
});
この方式は実際の import を追加します。もし純粋なタイプ宣言ファイル( @typedef のみの .js、d.ts に類似)であれば、JSDoc 方式は無用なファイル(コメントのみを含む)を導入することになりますが、TypeScript 方式にはこの問題が存在しません
P.S.TypeScript は同時にこれら 2 種類のタイプ導入構文と互換します。より多くの関連討論は Question: Import typedef from another file? を参照
タイプ変換
タイプ変換(TypeScript 中の [タイプアサーション](/articles/基本类型-typescript 笔记 2/#articleHeader4))構文は JSDoc と一致し、丸括弧前の @type マークを通じて丸括弧内の式タイプを説明します:
/** @type {!MyType} */ (valueExpression)
例えば:
/** @type {number | string} */
var numberOrString = Math.random() < 0.5 ? "hello" : 100;
var typeAssertedNumber = /** @type {number} */ (numberOrString)
// 错误 Type '"hello"' is not assignable to type 'number'.
typeAssertedNumber = 'hello';
P.S.注意、丸括弧が必須 です。否则認識されません
四.一般的なタイプ
オブジェクト
一般に @typedef マークを使用してオブジェクトタイプを記述します。例えば:
/**
* The complete Triforce, or one or more components of the Triforce.
* @typedef {Object} WishGranter~Triforce
* @property {boolean} hasCourage - Indicates whether the Courage component is present.
* @property {boolean} hasPower - Indicates whether the Power component is present.
* @property {boolean} hasWisdom - Indicates whether the Wisdom component is present.
*/
TypeScript タイプと同等です:
interface WishGranter {
hasCourage: boolean;
hasPower: boolean;
hasWisdom: boolean;
}
// または
type WishGranter = {
hasCourage: boolean;
hasPower: boolean;
hasWisdom: boolean;
}
もし一回限りのタイプ宣言(再利用不要、追加でタイプを定義したくない)であれば、 @param マークを使用して宣言できます。options.prop1 形式の属性名を通じてメンバー属性のネスト関係を記述します:
/**
* @param {Object} options - The shape is the same as SpecialType above
* @param {string} options.prop1
* @param {number} options.prop2
* @param {number=} options.prop3
* @param {number} [options.prop4]
* @param {number} [options.prop5=42]
*/
function special(options) {
return (options.prop4 || 1001) + options.prop5;
}
関数
@typedef マークでオブジェクトを記述するのと同様に、@callback マークを使用して関数のタイプを記述できます:
/**
* @callback Predicate
* @param {string} data
* @param {number} [index]
* @returns {boolean}
*/
/** @type {Predicate} */
const ok = s => !(s.length % 2);
TypeScript コードと同等です:
type Predicate = (data: string, index?: number) => boolean
また @typedef 特殊構文(TypeScript のみサポート、JSDoc にはありません)を使用してオブジェクトまたは関数のタイプ定義を 1 行に統合できます:
/** @typedef {{ prop1: string, prop2: string, prop3?: number }} SpecialType */
/** @typedef {(data: string, index?: number) => boolean} Predicate */
// TypeScript コードと同等
type SpecialType = {
prop1: string;
prop2: string;
prop3?: number;
}
type Predicate = (data: string, index?: number) => boolean
パラメータ
関数パラメータは @param マークを通じて記述し、@type 構文と同じですが、パラメータ名が 1 つ追加されます。例えば:
/**
* @param {string} p1 必須パラメータ
*/
function f(p1) {}
オプションパラメータには 3 種類の表現方式があります:
/**
* @param {string=} p1 - オプションパラメータ(Closure 構文)
* @param {string} [p2] - オプションパラメータ(JSDoc 構文)
* @param {string} [p3 = 'test'] - デフォルト値のあるオプションパラメータ(JSDoc 構文)
*/
function fn(p1, p2, p3) {}
P.S.注意、接尾等号構文({string=} など)はオブジェクトリテラルタイプには適用されません。例えば @type {{ a: string, b: number= }} は非法なタイプ宣言で、オプション属性は属性名接尾 ? で表現すべきです
可変長パラメータには 2 種類の表現方式があります:
/**
* @param {...string} p - A 'rest' arg (array) of strings. (treated as 'any')
*/
function fn(p){ arguments; }
/** @type {(...args: any[]) => void} */
function f() { arguments; }
戻り値
戻り値のタイプ注釈方式も類似しています:
/**
* @return {PromiseLike<string>}
*/
function ps() {
return Promise.resolve('');
}
/**
* @returns {{ a: string, b: number }}
*/
function ab() {
return {a: 'a', b: 11};
}
P.S. @returns と @return は完全に同等で、後者は前者のエイリアスです
クラス
コンストラクタ
タイプシステムは this への属性代入に基づいてコンストラクタを推論し、 @constructor マークを通じてコンストラクタを記述することもできます
二者の違いは @constructor マークがある場合、タイプチェックがより厳格 になります。具体的には、コンストラクタ中の this 属性アクセスおよびコンストラクタパラメータをチェックし、(new キーワードを通じずに)コンストラクタを直接呼び出すことを許可しません:
/**
* @constructor
* @param {number} data
*/
function C(data) {
this.size = 0;
// 错误 Argument of type 'number' is not assignable to parameter of type 'string'.
this.initialize(data);
}
/**
* @param {string} s
*/
C.prototype.initialize = function (s) {
this.size = s.length
}
var c = new C(0);
// 错误 Value of type 'typeof C' is not callable. Did you mean to include 'new'?
var result = C(1);
P.S. @constructor マークを削除すれば、これら 2 つのエラーは報告されません
さらに、コンストラクタまたはクラスタイプのパラメータに対して、TypeScript 構文に類似した方式でそのタイプを記述できます:
/**
* @template T
* @param {{new(): T}} C コンストラクタ C が必ず同一クラス(またはサブクラス)のインスタンスを返すことを要求
* @returns {T}
*/
function create(C) {
return new C();
}
P.S.JSDoc は Newable パラメータを記述する方式を提供していません。詳細は Document class types/constructor types を参照
this タイプ
ほとんどの場合、タイプシステムはコンテキストに基づいて this のタイプを推論できます。複雑なシナリオでは @this マークを通じて this のタイプを明示的に指定できます:
// 推論タイプは function getNodeHieght(): any
function getNodeHieght() {
return this.innerHeight;
}
// this タイプを明示的に指定、推論タイプは function getNodeHieght(): number
/**
* @this {HTMLElement}
*/
function getNodeHieght() {
return this.clientHeight;
}
継承
TypeScript では、クラスの継承関係は JSDoc を通じて記述できません:
class Animal {
alive = true;
move() {}
}
/**
* @extends {Animal}
*/
class Duck {}
// 错误 Property 'move' does not exist on type 'Duck'.
new Duck().move();
@augments(または @extends)は基底クラスのジェネリックパラメータを指定するためにのみ使用されます:
/**
* @template T
*/
class Box {
/**
* @param {T} value
*/
constructor(value) {
this.value = value;
}
unwrap() {
return this.value;
}
}
/**
* @augments {Box<string>} 記述
*/
class StringBox extends Box {
constructor() {
super('string');
}
}
new StringBox().unwrap().toUpperCase();
しかし JSDoc と不同的是、 @arguments/extends マークはClass のみに使用可能で、コンストラクタには適用されません:
/**
* @constructor
*/
function Animal() {
this.alive = true;
}
/**
* @constructor
* @augments Animal
*/
// 错误 JSDoc ' @augments' is not attached to a class.
function Duck() {}
Duck.prototype = new Animal();
したがって、 @augments/extends マークの作用は非常に弱く、非 Class 継承を記述できず、継承関係を決定することもできません(継承関係は extends 句によって決定され、JSDoc の記述はカウントされません)
列挙
列挙は @enum マークを使用して記述しますが、TypeScript 列挙タイプ とは異なり、主な差異は:
-
列挙メンバーのタイプが一致することを要求
-
しかし列挙メンバーは任意のタイプ可以是
例えば:
/** @enum {number} */
const JSDocState = {
BeginningOfLine: 0,
SawAsterisk: 1,
SavingComments: 2,
}
/** @enum {function(number): number} */
const SimpleMath = {
add1: n => n + 1,
id: n => n,
sub1: n => n - 1,
}
ジェネリック
ジェネリックは @template マークを使用して記述します:
/**
* @template T
* @param {T} x - A generic parameter that flows through to the return type
* @return {T}
*/
function id(x) { return x }
let x = id('string');
// 错误 Type '0' is not assignable to type 'string'.
x = 0;
TypeScript コードと同等です:
function id<T>(x: T): T {
return x;
}
let x = id('string');
x = 0;
複数の [タイプパラメータ](/articles/泛型-typescript 笔记 6/#articleHeader3) がある場合、カンマで区切るか、複数の @template タグを使用できます:
/**
* @template T, U
* @param {[T, U]} pairs 二項組
* @returns {[U, T]}
*/
function reversePairs(pairs) {
const x = pairs[0];
const y = pairs[1];
return [y, x];
}
// 同等
/**
* @template T
* @template U
* @param {[T, U]} pairs 二項組
* @returns {[U, T]}
*/
function reversePairs(pairs) {
const x = pairs[0];
const y = pairs[1];
return [y, x];
}
さらに、[ジェネリック制約](/articles/泛型-typescript 笔记 6/#articleHeader8) もサポートします:
/**
* @typedef Lengthwise
* @property length {number}
*/
/**
* @template {Lengthwise} T
* @param {T} arg
* @returns {T}
*/
function loggingIdentity(arg) {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
TypeScript コードと同等です:
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // Now we know it has a .length property, so no more error
return arg;
}
特別に、 @typedef マークと結合してジェネリックタイプを定義する際、必ず先にジェネリックパラメータを定義 する必要があります:
/**
* @template K
* @typedef Wrapper
* @property value {K}
*/
/** @type {Wrapper<string>} */
var s;
s.value.toLocaleLowerCase();
@template と @typedef の順序は逆転できません。否则 エラーが報告されます:
JSDoc ' @typedef' tag should either have a type annotation or be followed by ' @property' or ' @member' tags.
TypeScript ジェネリック宣言と同等です:
type Wrapper<K> = {
value: K;
}
Nullable
JSDoc 中では、明示的に Null 可能タイプと非 Null タイプを指定できます。例えば:
-
{?number}:number | nullを表す -
{!number}:numberを表す
しかし TypeScript 中では明示的に指定できません。タイプが Null を含むかどうかは --strictNullChecks オプションのみに依存 します:
/**
* @type {?number}
* strictNullChecks を开启時、タイプは number | null
* strictNullChecks を关闭時、タイプは number
*/
var nullable;
/**
* @type {!number} 明示的に非 Null を指定しても無効、strictNullChecks オプションのみに依存
*/
var normal;
コメントはまだありません