AI can fly !!

AI がやりたい Web エンジニアのアウトプット (AI の知識は無い)

【TypeScript】そろそろ TSDoc を始めてみる

TSDoc

はじめに

それなりに TypeScript を書いてきて、ぼちぼち初心者という枠を出るか出ないかというところまできたので (とは言ってもまだまだ初級者) 、ちょうど仕事でドキュメンテーションしっかりしようかっていう流れもあり、今まで雰囲気で書いてきたコメント (アノテーション) を見直すタイミングがやってきました。

JavaScript のコメント (アノテーション) は JSDoc がデファクトスタンダードになっていて、 TypeScript は今のところコレというものは決まっていないようでしたが、今回は JSDoc の TypeScript 版である TSDoc をマークアップ言語として選択しました。

TSDoc は MicrosoftOSS として公開しており、おそらく今後は JSDoc のように TypeScript アノテーションデファクトスタンダードになるであろうと (個人的に) 思ったからです。

2021 年 8 月現在、あまり日本語で書かれた記事を見付けられなかったため、 TSDoc の公式ドキュメント からの引用を中心に、とりあえずこれが分かればなんとか書けそうという内容をまとめてみました。

TL;DR

  • TSDoc は TypeScript のコメント (アノテーション) を標準化するための提案 (今のところ) だよ
  • JSDoc と違って、公式からドキュメント出力してくれるパッケージとかは出てないよ
  • 細かいことは置いといて、使用例だけ見たいって人は ここ を見てね

TSDoc とは

tsdoc.org

前述した通り、 JavaScript のコメント (アノテーション) を書くマークアップ言語のデファクトスタンダードである JSDoc の TypeScript 版として、 Microsoft から OSS として公開されている TypeScript アノテーション用のマークアップ言語です。

JSDoc はデファクトスタンダードとされつつも、どうやら標準化は行われていないようで (本当?) 、 JSDoc で書かれたコメントからドキュメントを出力できる同名の npm パッケージ (jsdoc) の実装がある意味本体となっているようです。 (本当??)

TSDoc はルール (構文) の標準化を目的としており、 TSDoc で書かれたコメントのパース (解析) を行うエンジンコンポーネント (npm パッケージ: @microsoft/tsdoc) や、 ESLint 用のプラグイン (npm パッケージ: eslint-plugin-tsdoc) なども公開されています。

注意点として、 JSDoc と違い、 npm パッケージの @microsoft/tsdoc はパーサーとしてコードや他のツールなどに組み込まれる前提のエンジンコンポーネントなので、 TSDoc で書かれたコメントをドキュメントとして出力したい場合は、別途ツールやパッケージを使用する必要があります。

TSDoc 構文

TSDoc では構文として定められたタグを使用し、アノテーションを追加したい変数や関数などの直前にコメントを記述します。

@microsoft/tsdoc のパーサーに認識してもらうためには、アノテーション/** で始める必要があります。

export class TSDocSyntaxExample() {
  /**
   * 要約
   *
   * @remarks
   * 詳細な説明
   *
   * @param name - パラメータの説明
   * @returns 戻り値の説明
   *
   * @beta (← 装飾タグ)
   */
  public helloMessage(name: string): string {
    return 'Hello ' + name + ' !!';
  }
}

タグ

タグは @ から始まり、キャメルケース (camelCase) で記述します。

TSDoc で定められているタグは、後述する三種類のいずれかに分類されます。

また、各タグは TSDoc が互換性のある各種ドキュメントツールから使用される際に、サポートが期待されるレベルに応じて、同じく後述する三つの標準化グループに分類されます。

タグの種類 (Tag kinds)

tsdoc.org

Block tags (ブロックタグ)

一部のタグを除き、ブロックタグは先頭行にタグ、そして次の行からタグの内容 (タグコンテンツ) を記述します。

ブロックタグから次のブロックタグ、またはモディファイタグまでがタグコンテンツと解釈されます。

ブロックタグに続けてタグコンテンツを一行で書いたり、ブロックタグの次行から複数行に渡ってタグコンテンツを書いたりします。

/**
 * This is the special summary section.
 *
 * @remarks
 * This is a standalone block.
 *
 * @example Logging a warning
 * ```ts
 * logger.warn('Something happened');
 * ```
 *
 * @example Logging an error
 * ```ts
 * logger.error('Something happened');
 * ```
 */

Modifier tags (モディファイタグ)

モディファイタグは通常、コメント (アノテーション) の最下部一行にタグのみを記述し、アノテーション対象を装飾します。

一行に複数のモディファイタグを記述することもできます。

仮にモディファイタグにタグコンテンツが記載されていた場合、パーサーは無視 (破棄) をするか、互換性が向上する場合*1に限り、直前のブロックタグに関連付けられます。

/**
 * This is the special summary section.
 *
 * @remarks
 * This is a standalone block.
 *
 * @public @sealed
 */

Inline tags (インラインタグ)

インラインタグは、ブロックタグのタグコンテンツ内の要素として、常に中括弧 ({}) に囲んで記述します。

class Book {
  /**
   * Writes the book information into a JSON file.
   *
   * @remarks
   * This method saves the book information to a JSON file conforming to the standardized
   * {@link http://example.com/ | Example Book Interchange Format}.
   */
  public writeFile(options?: IWriteFileOptions): void {
    . . .
  }

  /**
   * {@inheritDoc Book.writeFile}
   * @deprecated Use {@link Book.writeFile} instead.
   */
  public save(): void {
    . . .
  }
}

標準化グループ (Standardization groups)

tsdoc.org

Core (コアグループ)

Enum value: Standardization.Core

Core に分類されるタグは必須タグとして標準化され、すべてのツールがこれらをサポートすることが期待されています。

TSDoc パーサ (@microsoft/tsdoc) はこれらのタグへアクセスするための専用 API を提供します。

Extended (拡張グループ)

Enum value: Standardization.Extended

Extended に分類されるタグはオプションとされ、ツールによってサポートする場合としない場合がありますが、サポートする場合は TSDoc の構文と定義された役割に準拠する必要があります。

Discretionary (任意グループ)

Enum value: Standardization.Discretionary

Discretionary に分類されるタグも Extended 同様にオプションとなり、構文は TSDoc によって指定されていますが、意味・役割は実装依存 (ツールにより異なる) になります。

ただ、同じタグは異なるツールであっても、おそらく似た意味で使用されていると予想されています。 (が、やはり必ず同義であることは保証されていません。)

タグリファレンス

TSDoc で定義されているタグを、標準化グループ毎、かつ、タグ種別ごとにまとめました。

各タグで例として記載しているコードは、 TSDoc 公式ドキュメント からの引用がベースとなりますので、安心して参考にしてください。

ただし、いつものことですが、各タグの解説は TSDoc 公式ドキュメント の僕の超訳でお送りします。

解説には、サードパーティ製の各ドキュメントツールから各タグがどのような内容であることを期待されているのか、また、どのように解釈されるのかも、書いたり書かなかったりしています。

Core (コアグループ)

Block tags (ブロックタグ)

@deprecated (非推奨)

TSDoc: @deprecated

/**
 * The base class for controls that can be rendered.
 *
 * @deprecated Use the new {@link Control} base class instead.
 */
export class VisualControl {
  . . .
}

API が非推奨 (サポート対象外) となり、将来的に削除される可能性があることを表します。

@deprecated タグの後に、推奨される代替手段を記述します。

アノテーションの対象が class の場合、クラス自体が非推奨となるため、クラス内のプロパティやメソッドもすべて非推奨となります。

@param (引数)

TSDoc: @param

/**
 * Returns the average of two numbers.
 *
 * @remarks
 * This method is part of the {@link core-library#Statistics | Statistics subsystem}.
 *
 * @param x - The first input number
 * @param y - The second input number
 * @returns The arithmetic mean of `x` and `y`
 *
 * @beta
 */
function getAverage(x: number, y: number): number {
  return (x + y) / 2.0;
}

アノテーションの対象である関数の引数を表します。

@param タグの後に、 パラメータ名 - 説明 の形式で記述します。

@privateRemarks (非公開の備考)

TSDoc: @privateRemarks

/**
 * The summary section should be brief. On a documentation web site,
 * it will be shown on a page that lists summaries for many different
 * API items.  On a detail page for a single item, the summary will be
 * shown followed by the remarks section (if any).
 *
 * @remarks
 *
 * The main documentation for an API item is separated into a brief
 * "summary" section optionally followed by an `@remarks` block containing
 * additional details.
 *
 * @privateRemarks
 *
 * The `@privateRemarks` tag starts a block of additional commentary that is not meant
 * for an external audience.  A documentation tool must omit this content from an
 * API reference web site.  It should also be omitted when generating a normalized *.d.ts file.
 */

一版ユーザ向けではない追加のドキュメントであることを表します。

@remarks (備考)

TSDoc: @remarks

/**
 * The summary section should be brief. On a documentation web site,
 * it will be shown on a page that lists summaries for many different
 * API items.  On a detail page for a single item, the summary will be
 * shown followed by the remarks section (if any).
 *
 * @remarks
 *
 * The main documentation for an API item is separated into a brief
 * "summary" section optionally followed by an `@remarks` block containing
 * additional details.
 *
 * @privateRemarks
 *
 * The `@privateRemarks` tag starts a block of additional commentary that is not meant
 * for an external audience.  A documentation tool must omit this content from an
 * API reference web site.  It should also be omitted when generating a normalized *.d.ts file.
 */

API のメインドキュメントは要約を記載するにとどめ、詳細な説明は @remarks セクションに記載します。

アノテーションの先頭から @remarks タグまでがメインドキュメントの要約セクションとなり、 @remarks タグ以降が詳細な説明を記述する備考セクションであることを表します。

@returns (戻り値)

TSDoc: @returns

/**
 * Returns the average of two numbers.
 *
 * @remarks
 * This method is part of the {@link core-library#Statistics | Statistics subsystem}.
 *
 * @param x - The first input number
 * @param y - The second input number
 * @returns The arithmetic mean of `x` and `y`
 *
 * @beta
 */
function getAverage(x: number, y: number): number {
  return (x + y) / 2.0;
}

アノテーションの対象である関数の戻り値を表します。

@typeParam (型引数)

TSDoc: @typeParam

/**
 * Alias for array
 *
 * @typeParam T - Type of objects the list contains
 */
type List<T> = Array<T>;

/**
 * Wrapper for an HTTP Response
 * @typeParam B - Response body
 * @typeParam <H> - Headers
 */
interface HttpResponse<B, H> {
    body: B;
    headers: H;
    statusCode: number;
}

アノテーションの対象であるジェネリックの型引数を表します。

@typeParam タグの後に、 パラメータ名 - 説明 の形式で記述します。

Modifier tags (モディファイタグ)

@packageDocumentation

TSDoc: @packageDocumentation

// Copyright (c) Example Company. All rights reserved. Licensed under the MIT license.

/**
 * A library for building widgets.
 *
 * @remarks
 * The `widget-lib` defines the {@link IWidget} interface and {@link Widget} class,
 * which are used to build widgets.
 *
 * @packageDocumentation
 */

/**
 * Interface implemented by all widgets.
 * @public
 */
export interface IWidget {
  /**
   * Draws the widget on the screen.
   */
  render(): void;
}

アノテーションが npm パッケージ全体に対して記述するコメントであることを示します。

@packageDocumentation タグは個々の API に対してではなく、パッケージのエントリーポイントにあたるファイルの先頭のコメントに対してのみ使用することができます。

Inline tags (インラインタグ)

@label (ラベル)

TSDoc: @label

export interface Interface {
  /**
   * Shortest name:  {@link InterfaceL1.(:STRING_INDEXER)}
   * Full name:      {@link (InterfaceL1:interface).(:STRING_INDEXER)}
   *
   * {@label STRING_INDEXER}
   */
  [key: string]: number;

  /**
   * Shortest name:  {@link InterfaceL1.(:FUNCTOR)}
   * Full name:      {@link (InterfaceL1:interface).(:FUNCTOR)}
   *
   * {@label FUNCTOR}
   */
  (source: string, subString: string): boolean;

  /**
   * Shortest name:  {@link InterfaceL1.(:CONSTRUCTOR)}
   * Full name:      {@link (InterfaceL1:interface).(:CONSTRUCTOR)}
   *
   * {@label CONSTRUCTOR}
   */
  new (hour: number, minute: number);
}

@label タグは、変数やフィールド、プロパティなどの宣言にラベルを付けるために使用されます。

ラベル付けされた宣言は、 @link タグから参照することができます。

TSDoc: @link

/**
 * Let's learn about the `{@link}` tag.
 *
 * @remarks
 *
 * Links can point to a URL: {@link https://github.com/microsoft/tsdoc}
 *
 * Links can point to an API item: {@link Button}
 *
 * You can optionally include custom link text: {@link Button | the Button class}
 *
 * Suppose the `Button` class is part of an external package.  
 * In that case, we can include the package name when referring to it:
 *
 * {@link my-control-library#Button | the Button class}
 *
 * The package name can include an NPM scope and import path:
 *
 * {@link @microsoft/my-control-library/lib/Button#Button | the Button class}
 *
 * We can refer to a member of the class:
 *
 * {@link controls.Button.render | the render() method}
 *
 * If a static and instance member have the same name, we can use a selector to distinguish them:
 *
 * {@link controls.Button.(render:instance) | the render() method}
 *
 * {@link controls.Button.(render:static) | the render() static member}
 *
 * This is also how we refer to the class's constructor:
 *
 * {@link controls.(Button:constructor) | the class constructor}
 */

API のメンバや @label タグ、インターネット上の URL へのハイパーリンクを作成するために使用します。

実際にハイパーリンクを生成するためにはドキュメント生成ツールなどが必要ですが、 @link タグ自体は他リソースへの参照を表すことができます。

Extended (拡張グループ)

Block tags (ブロックタグ)

@decorator (デコレータ)

TSDoc: @decorator

class Book {
  /**
   * The title of the book.
   * @decorator `@jsonSerialized`
   * @decorator `@jsonFormat(JsonFormats.Url)`
   */
  @jsonSerialized
  @jsonFormat(JsonFormats.Url)
  public website: string;
}

API にデコレータが使用されていることを表します。

これは TypeScript のコンパイラが出力する型定義ファイル (*.d.ts) にデコレータが含まれないため、その回避策として用意されたタグになります。

@defaultValue (既定値)

TSDoc: @defaultValue

enum WarningStyle {
  DialogBox,
  StatusMessage
}

interface IWarningOptions {
  /**
   * Determines how the warning will be displayed.
   *
   * @remarks
   * See {@link WarningStyle| the WarningStyle enum} for more details.
   *
   * @defaultValue `WarningStyle.DialogBox`
   */
  warningStyle: WarningStyle;

  /**
   * Whether the warning can interrupt a user's current activity.
   * @defaultValue
   * The default is `true` unless
   *  `WarningStyle.StatusMessage` was requested.
   */
  cancellable?: boolean;
}

@defaultValue タグは class または interface のメンバーであるフィールドかプロパティに対してのみ使用することが可能で、明示的に値が割り当てられていない場合の既定値を表します。

書き方は二通りあり、既定値を @defaultValue タグの後に記載するか、次の行からタグコンテンツとして既定値の説明文を記述します。

@example (使用例)

TSDoc: @example

/**
 * Adds two numbers together.
 * @example
 * Here's a simple example:
 * ```
 * // Prints "2":
 * console.log(add(1,1));
 * ```
 * @example
 * Here's an example with negative numbers:
 * ```
 * // Prints "0":
 * console.log(add(1,-1));
 * ```
 */
export function add(x: number, y: number): number {
}

/**
 * Parses a JSON file.
 *
 * @param path - Full path to the file.
 * @returns An object containing the JSON data.
 *
 * @example Parsing a basic JSON file
 *
 * # Contents of `file.json`
 * ```json
 * {
 *   "exampleItem": "text"
 * }
 * ```
 *
 * # Usage
 * ```ts
 * const result = parseFile("file.json");
 * ```
 *
 * # Result
 * ```ts
 * {
 *   exampleItem: 'text',
 * }
 * ```
 */
export function parseFile(path: string): object {
}

API の使用方法を表します。

@example タグと同じ行の後続テキストはその例のタイトルとして解釈され、複数の @example タグを記載した場合は、各使用例をインデックスで表すことができます。

上の例のタイトルは ExampleExample2 、下の例のタイトルは Example: Parsing a basic JSON file になります。

また、タグコンテンツには Markdown でコードサンプルを記述することができます。

@see (参照先)

TSDoc: @see

/**
 * Parses a string containing a Uniform Resource Locator (URL).
 * @see {@link ParsedUrl} for the returned data structure
 * @see {@link https://tools.ietf.org/html/rfc1738|RFC 1738} for syntax
 * @see your developer SDK for code samples
 * @param url - the string to be parsed
 * @returns the parsed result
 */
function parseURL(url: string): ParsedUrl;

API に関連する他の API やリソースへの参照リストを作成するために使用します。

@see タグと同じ行の後に、参照先のリンクや説明文を記述します。

@throws (例外)

TSDoc: @throws

/**
 * Retrieves metadata about a book from the catalog.
 *
 * @param isbnCode - the ISBN number for the book
 * @returns the retrieved book object
 *
 * @throws {@link IsbnSyntaxError}
 * This exception is thrown if the input is not a valid ISBN number.
 *
 * @throws {@link book-lib#BookNotFoundError}
 * Thrown if the ISBN number is valid, but no such book exists in the catalog.
 *
 * @public
 */
function fetchBookByIsbn(isbnCode: string): Book;

アノテーションの対象である関数やプロパティからスローされる例外の型を表します。

スローされる可能性のある例外が複数存在する場合、それぞれ個別に @throws タグを使用する必要があります。

@throws タグと同じ行の後に例外の名前を記述しますが、必須ではありません。

Modifier tags (モディファイタグ)

@eventProperty (イベントプロパティ)

TSDoc: @eventProperty

class MyClass {
  /**
    * This event is fired whenever the application navigates to a new page.
    * @eventProperty
    */
  public readonly navigatedEvent: FrameworkEvent<NavigatedEventArgs>;
}

classinterface のプロパティが、アタッチ可能なイベントオブジェクトを返すことを表します。

戻り値のイベントオブジェクトには、 addHandler()removeHandler() といったイベントハンドラを操作するメソッドを持つクラスであることが期待されます。

@override (再定義・オーバーライド)

TSDoc: @override

class Base {
  /** @virtual */
  public render(): void { // サブクラスによって再定義される可能性がある
  }

  /** @sealed */
  public initialize(): void { // サブクラスで再定義してはいけない
  }
}

class Child extends Base {
  /** @override */
  public render(): void; // 基本クラスの定義を再定義している
}

アノテーションの対象がメンバー関数やプロパティの場合、この定義が基本クラスから継承された定義をオーバーライド (再定義) していることを表します。

通常、再定義される可能性のある基本クラスの定義には @virtual タグを記述します。

@readonly (読み取り専用)

TSDoc: @readonly

export class Book {
  /**
   * Technically property has a setter,
   * but for documentation purposes it should be presented as readonly.
   * @readonly
   */
  public get title(): string {
    return this._title;
  }

  public set title(value: string) {
    throw new Error('This property is read-only!');
  }
}

TypeScript の構文として読み取り専用であるかどうかに関わらず、仕様として API が読み取り専用であることを表します。

上の例では、 Setter 関数でプロパティに値を割り当てていないため、 Book.title が読み取り専用であることを示しています。

@virtual (再定義の可能性有)

TSDoc: @virtual

class Base {
  /** @virtual */
  public render(): void { // サブクラスによって再定義される可能性がある
  }

  /** @sealed */
  public initialize(): void { // サブクラスで再定義してはいけない
  }
}

class Child extends Base {
  /** @override */
  public render(): void; // 基本クラスの定義を再定義している
}

アノテーションの対象がメンバー関数やプロパティの場合、この定義をサブクラスがオーバーライド (再定義) する可能性があることを表します。

@sealed (継承・再定義不可)

TSDoc: @sealed

class Base {
  /** @virtual */
  public render(): void { // サブクラスによって再定義される可能性がある
  }

  /** @sealed */
  public initialize(): void { // サブクラスで再定義してはいけない
  }
}

class Child extends Base {
  /** @override */
  public render(): void; // 基本クラスの定義を再定義している
}

アノテーションの対象がクラスの場合は継承不可、メンバー関数やプロパティの場合はサブクラスがオーバーライド (再定義) してはならないことを表します。

Inline tags (インラインタグ)

@inheritDoc (ドキュメントの引用)

TSDoc: @inheritDoc

import { Serializer } from 'example-library';

/**
 * An interface describing a widget.
 * @public
 */
export interface IWidget {
  /**
   * Draws the widget on the display surface.
   * @param x - the X position of the widget
   * @param y - the Y position of the widget
   */
  public draw(x: number, y: number): void;
}

/** @public */
export class Button implements IWidget {
  /** {@inheritDoc IWidget.draw} */
  public draw(x: number, y: number): void {
    . . .
  }

  /**
   * {@inheritDoc example-library#Serializer.writeFile}
   * @deprecated Use {@link example-library#Serializer.writeFile} instead.
   */
  public save(): void {
    . . .
  }
}

対象の API に対し、他のクラスや npm パッケージの APIアノテーションを引用 (コピー) していることを表します。

コピー対象のタグ (コメント) は、

  • 要約セクション
  • @remark
  • @params
  • @typeParam
  • @return

のみとなり、コピー対象外のタグについては @inheritDoc タグ以降に記述する必要があります。

@inheritDoc タグを記述した場合、そのアノテーションにはメインコメントである要約や @remark タグは記述できません。

Discretionary (任意グループ)

Modifier tags (モディファイタグ)

@alpha (アルファ版・未リリース)

TSDoc: @alpha

/**
 * Represents a book in the catalog.
 * @public
 */
export class Book { // Book クラスはリリース済み
  /**
   * The title of the book.
   * @alpha
   */
  public get title(): string; // Book.title は未リリースのアルファ版

  /**
   * The author of the book.
   */
  public get author(): string; // Book.author も Book を継承してリリース済み
};

API がアルファ版 (未リリース) であることを表します。

API を使用することは可能ですが、あくまでもまだリリースはしていないことを示す場合に使用します。

@beta (ベータ版・実験的リリース)

TSDoc: @beta

/**
 * Represents a book in the catalog.
 * @public
 */
export class Book { // Book クラスはリリース済み
  /**
   * The title of the book.
   * @beta
   */
  public get title(): string; // Book.title は実験的リリースのベータ版

  /**
   * The author of the book.
   */
  public get author(): string; // Book.author も Book を継承してリリース済み
};

API がベータ版 (実験的リリース) であることを表します。

目的がフィードバック収集の場合など、リリースはされたものの、まだ運用には適さないことを示す際に使用します。

@experimental (実験段階)

TSDoc: @experimental

/**
 * Represents a book in the catalog.
 * @public
 */
export class Book { // Book クラスはリリース済み
  /**
   * The title of the book.
   * @experimental
   */
  public get title(): string; // Book.title は実験段階

  /**
   * The author of the book.
   */
  public get author(): string; // Book.author も Book を継承してリリース済み
};

@alpha@beta と同様に、未リリースの実験段階であることを表します。

リリースステージとしては、 @experimental@alpha@beta と進みます。 (と思われます。)

@internal (内部用)

TSDoc: @internal

/**
 * Represents a book in the catalog.
 * @public
 */
export class Book { // Book クラスはリリース済み
  /**
   * The title of the book.
   * @internal
   */
  public get _title(): string; // Book._title は内部用

  /**
   * The author of the book.
   */
  public get author(): string; // Book.author も Book を継承してリリース済み
};

API が外部から使用される予定が無いことを表します。

publicprivate などのアクセス修飾子による違いではなく、そもそも API 自体が使用されないということを示しています。

@public (公開版・リリース済)

TSDoc: @public

/**
 * Represents a book in the catalog.
 * @public
 */
export class Book { // Book クラスはリリース済み
  /**
   * The title of the book.
   * @internal
   */
  public get _title(): string; // Book._title は内部用

  /**
   * The author of the book.
   */
  public get author(): string; // Book.author も Book を継承してリリース済み
};

API が公開されているリリース版であることを表します。

使用例

ここまで長々と書きましたが、結局どう書いたらいいの?ということで、 Angular のコードにアノテーションを付けてみました。 (コードは適当なので、そこへのつっこみは無しの方向で…)

簡易なアノテーションから詳細なアノテーションまでいくつかのパターンで記載していますので、この中からプロジェクトやチームに合った形を見付けて参考にしていただければと思います。

これが正解というわけではないので、とりあえずこんな感じでスタートして、徐々にチーム内でブラッシュアップしていけばいいんじゃないでしょうか。

正直、正解はよく分からん…

クラス (Class)

/**
 * ユーザ情報クラスコンポーネント
 *
 * @remarks
 * ユーザ情報を表示する画面を構成するコンポーネントです。
 *
 * @decorator `@Component()`
 *
 * @public
 */
@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.scss']
})
export class UserComponent implements OnInit {
  /**
   * ユーザ ID
   *
   * @remarks
   * ユーザ ID は親コンポーネントから渡されます。
   *
   * @decorator `@Input()`
   */
  @Input()
  userId: string = '';

  /**
   * ユーザ名
   * 
   * @defaultValue `'unknown'`
   */
  userName: string;

  constructor(
    private usrSvc: userService
  ) {
    this.userName = 'unknown';
  }

  /** @override */
  ngOnInit(): void {
    this.showUserName(this.userId);
  }

  /**
   * ユーザ名を表示する。
   *
   * @remarks
   * パラメータとして与えられたユーザ ID からユーザ名を取得し、
   * ユーザ名プロパティへ値をセットします。
   * 
   * @privateRemarks
   * ユーザ情報の型 {@link (:USER) | User model}
   *
   * @param id - ユーザ ID
   *
   * @public
   */
  private showUserName(id: string): void {
    if (id === '') {
      return;
    }

    this.usrSvc.getName<User>(id).subscribe(
      (user: User): void => {
        this.userName = user.name;
      }
    );
  }
}

インターフェース (Interface)

/**
 * ユーザ情報
 *
 * {@label USER}
 *
 * @public
 */
export interface User {
  /** ユーザ ID */
  id: string;
  /** ユーザ名 */
  name: string;
}

ジェネリック (Generics)

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(
    private http: HttpClient
  ) {}

  /**
   * ユーザ名を取得する。
   *
   * @remarks
   * パラメータとして与えられたユーザ ID からユーザ名を含む
   * ユーザ情報を取得し、戻り値として返します。
   * 
   * @privateRemarks
   * ユーザ名だけでなく、ユーザ情報すべてを取得している。
   *
   * @typeParam T - ユーザ情報
   * @param id - ユーザ ID
   * @returns ユーザ名を含むユーザ情報
   *
   * @beta @sealed
   */
  getName<T>(id: string): Observable<T> {
    const options = new HttpParams().set('id', id);
    return this.http.get('/path/to/api/', options);
  }
}

おわりに

今回まとめるにあたって一通りすべてのタグに目を通しましたが、アノテーションにこれらのタグをフルスペックで記述したら、かなり充実するだろうなーと思いました。

プロジェクト要件にプログラム設計書の作成・納品などが含まれている場合、いちいちドキュメントを作成するくらいなら、 TSDoc でしっかりアノテーションを書いて、ツールでドキュメント出力をした方が確実で早いし簡単ですね。

良い意味で「ソースコードが仕様書です!」と言えるんじゃないでしょうか。

今まで悪い意味でしか使ったことが無いですが…

あとは JSDoc のように、 TSDoc 公式からドキュメント出力をしてくれる npm パッケージが公開されたら大変ありがたいのですが、 TSDoc のロードマップ にはそんなこと一切書かれていないし、 @microsoft/tsdoc はあくまでも TSDoc のパーサーという位置付けなんでしょうね。。

TypeDoc とか使えばいいんかな… _(-ω-`_)⌒)_

*1:どういう場合かよく分かりません…