banner
 Sayyiku

Sayyiku

Chaos is a ladder
telegram
twitter

TypeScript コーディング規範

TypeScript は、マイクロソフトによって開発された自由でオープンソースのプログラミング言語です。これは JavaScript のスーパーセットであり、本質的にこの言語にオプションの静的型付けとクラスベースのオブジェクト指向プログラミングを追加しています。

命名#

  1. 型の名前には PascalCase を使用します。これにはインターフェース interface、型エイリアス type、クラス class が含まれます。
  2. インターフェース名のプレフィックスとして I を使用しないでください。インターフェースメンバーは camelCase 方式で命名します。
// 悪い例
interface IFoo {
  Bar: number
  Baz(): number
}

// 良い例
interface Foo {
  bar: number
  baz(): number
}

なぜ I プレフィックスをインターフェースに使用しないのか?

  1. I プレフィックスはカプセル化の原則に違反します:TypeScript では、クラスはインターフェースを実装でき、インターフェースはインターフェースを継承でき、インターフェースはクラスを継承できます。クラスとインターフェースは、ある意味で抽象化とカプセル化の一種であり、継承時にそれがインターフェースであるかクラスであるかを気にする必要はありません。I プレフィックスを使用すると、変数の型が変更された場合、たとえばインターフェースからクラスに変わった場合、変数名も同期して変更する必要があります。
  2. 不適切な命名を防ぐ:I プレフィックスの使用を禁止することで、プログラマーはインターフェースに適切で意味のある、他の同種の変数と区別しやすい名前を付けることを強制されます。
  3. ハンガリアン命名法の時代は過ぎ去りました:ハンガリアン命名法は、型プレフィックスと実際の変数名で構成され、こうした方法で命名された変数は、変数名を見るだけでその型を即座に知ることができます。しかし、その欠点は得られる利点をはるかに上回ります。たとえば、変数名が冗長になり、同じ主体名でも型が異なる変数に曖昧さをもたらします。

例:

interface IFoo {}
class Point {}
type Baz = IFoo & Point

実際に私たちが気にしているのは、これは「型」であるかどうかです。インターフェース、クラス、または型であっても、「型」として扱われ、他のものにはプレフィックスを付ける必要はありません。インターフェースにプレフィックスを付けて独立させる必要はありません。

  1. インターフェースに "I" をプレフィックスすることの禁止
  2. TypeScript のインターフェースとクラスのコーディングガイドラインについて混乱している
  1. 列挙型のオブジェクト自体と列挙メンバーには PascalCase を使用します。
// 悪い例
enum color {
  red,
}

// 良い例
enum Color {
  Red,
}
  1. 関数には camelCase を使用します。
  2. プロパティやローカル変数には camelCase を使用します。
// 悪い例
const DiskInfo
function GetDiskInfo() {}

// 良い例
const diskInfo
function getDiskInfo() {}
  1. クラスには PascalCase を使用し、クラスメンバーには camelCase を使用します。
// 悪い例
class foo {}

// 良い例
class Foo {}

// 悪い例
class Foo {
  Bar: number
  Baz(): number {}
}

// 良い例
class Foo {
  bar: number
  baz(): number {}
}
  1. モジュールの名前空間をインポートする際には camelCase 命名法を使用し、ファイル名には snake_case 命名法を使用します。
import * as fooBar from './foo_bar'
  1. プライベートプロパティ名に _ プレフィックスを付けないでください。
  2. 可能な限り完全な単語のスペルを使用して命名します。

まとめ:

命名法分類
パスカル命名法(PascalCase)クラス、インターフェース、型、列挙、列挙値、型パラメータ
キャメルケース命名法(camelCase)変数、パラメータ、関数、メソッド、プロパティ、モジュールエイリアス
全大文字アンダースコア命名法(CONSTANT_CASE)グローバル定数

モジュール#

インポート#

TypeScript コードはパスを使用してインポートする必要があります。ここでのパスは、. または .. で始まる相対パスであるか、プロジェクトのルートディレクトリから始まる絶対パス(例:root/path/to/file)であることができます。

論理的に同じプロジェクトに属するファイルを参照する場合は、相対パス ./foo を使用し、絶対パス path/to/foo を使用しないでください。

親レベルの数を可能な限り制限する必要があります(../../../ のようなパスを避ける)。過剰なレベルはモジュールとパス構造を理解しにくくします。

import { Symbol1 } from 'google3/path/from/root'
import { Symbol2 } from '../parent/file'
import { Symbol3 } from './sibling'

ES6 と TypeScript では、インポート文には 4 つのバリエーションがあります:

インポートタイプ用途
モジュールimport * as foo from '...'TypeScript のインポート方法
分解import { SomeThing } from '...'TypeScript のインポート方法
デフォルトimport SomeThing from '...'外部コードの特別なニーズにのみ使用
副作用import '...'特定のライブラリの副作用を読み込むためにのみ使用(例:カスタム要素)
// こうするべきです!この2つのバリエーションのうち、より適切なものを選択してください(以下を参照)。
import * as ng from '@angular/core'
import { Foo } from './foo'

// デフォルトインポートは必要な場合にのみ使用します。
import Button from 'Button'

// 時には、特定のライブラリをインポートするのは、そのコードが実行されるときの副作用のためです。
import 'jasmine'
import '@polymer/paper-button'

使用シーンによって、モジュールインポートと分解インポートにはそれぞれの利点があります。

モジュールインポート:

  1. モジュールインポート文は、モジュール全体に名前を提供し、モジュール内のすべてのシンボルはこの名前を通じてアクセスされます。これにより、コードの可読性が向上し、モジュール内のすべてのシンボルが自動補完されるようになります。
  2. モジュールインポートはインポート文の数を減らし、命名の衝突の可能性を低下させ、インポートされるモジュールに簡潔な名前を提供することを許可します。

分解インポート文は、インポートされる各シンボルにローカル名を提供し、インポートされたシンボルを使用する際にコードをより簡潔にすることができます。

コード内で、名前の衝突を解決するためにリネームインポートを使用できます:

import { SomeThing as SomeOtherThing } from './foo'

以下のような場合に、リネームインポートが有用です:

  1. 他のインポートされたシンボルとの名前の衝突を避けるため。
  2. インポートされたシンボルの名前が自動生成されている。
  3. インポートされたシンボルの名前が自分自身を明確に説明できない場合、リネームによってコードの可読性を向上させる必要がある(例:RxJS の from 関数を observableFrom にリネーム)。

エクスポート#

コード内では、名前付きエクスポート宣言を使用する必要があります。デフォルトエクスポートを使用しないことで、すべてのインポート文が統一された形式に従うことが保証されます。

// 名前付きエクスポートを使用してください:
export class Foo {}

// X こうするべきではありません!デフォルトエクスポートを使用しないでください!
export default class Foo {}

なぜ?デフォルトエクスポートは、エクスポートされたシンボルに標準的な名前を提供しないため、メンテナンスの難易度を増し、可読性のリスクを低下させ、明確な利点をもたらしません。

// デフォルトエクスポートは以下のような欠点を引き起こします
import Foo from './bar' // この文は合法です。
import Bar from './bar' // この文も合法です。

名前付きエクスポートの利点は、コードがエクスポートされていないシンボルをインポートしようとした場合、そのコードがエラーを報告することです。たとえば、foo.ts に次のようなエクスポート宣言があるとします:

// こうするべきではありません!
const foo = 'blah'
export default foo

もし bar.ts に次のようなインポート文があるとします:

// コンパイルエラー!
import { fizz } from './foo'

これはコンパイルエラーを引き起こします: error TS2614: Module '"./foo"' has no exported member 'fizz'。逆に、もし bar.ts のインポート文が次のようであれば:

// こうするべきではありません!これは余分な変数 fizz を定義します!
import fizz from './foo'

結果は fizz === foo となり、これはしばしば期待に反し、デバッグが難しくなります。

#

宣言規範#

  1. 型 / 関数が複数のコンポーネントで共有される必要がない限り、エクスポートしないでください。
  2. ファイル内では、型定義は最初に置くべきです。

自動型推論#

型宣言を行う際は、TypeScript の自動型推論機能にできるだけ依存し、正しい型を推論できる場合は手動で宣言しないようにします。

  1. 基本型の変数は手動で型を宣言する必要はありません。
let foo = 'foo'
let bar = 2
let baz = false
  1. 参照型の変数は型が正しいことを保証する必要があり、不正確な場合は手動で宣言します。
// 自動推論
let foo = [1, 2] // number[]

// 明示的な宣言
// 悪い例
let bar = [] // any[]

// 良い例
let bar: number[] = []

ボックス型#

いかなる場合でも、これらのボックス型を使用すべきではありません。次の型 Number、String、Boolean、Object を使用しないでください。これらの型はボックス型を指し、使用すべき型 number、string、boolean、objectアンボックス型を指します。

// 悪い例
function reverse(s: String): String

// 良い例
function reverse(s: string): string

String を例にとると、これは undefined、null、void を含み、アンボックス型 string を表しますが、他のボックス型に対応するアンボックス型は含まれません。以下のコードを見てみましょう:

// 以下のコードは成立します
const tmp1: String = undefined
const tmp2: String = null
const tmp3: String = void 0
const tmp4: String = 'linbudu'

// 以下のコードは成立しません。なぜなら文字列型のアンボックス型ではないからです
const tmp5: String = 599
const tmp6: String = { name: 'linbudu' }
const tmp7: String = () => {}
const tmp8: String = []

null か undefined か?#

TypeScript コードでは undefined または null を使用して欠損値をマークできますが、どちらか一方を使用するという一般的なルールはありません。多くの JavaScript API は undefined を使用します(例: Map.get)、しかし DOM はより多く null を使用します(例: Element.getAttribute)。したがって、nullundefined の選択は現在のコンテキストに依存します。

  1. nullable/undefined 型のエイリアス

|null または |undefined を含むユニオン型のエイリアスを作成することは許可されません。このような nullable エイリアスは通常、空の値がアプリケーション内で層を成して伝播し、空の値が発生する原因を隠蔽します。また、このようなエイリアスは、クラスやインターフェース内の特定の値がいつ空になる可能性があるかを不確定にします。

したがって、コードはエイリアスを使用する際にのみ |null または |undefined を追加することを許可します。同時に、空の値が発生する位置の近くでそれを処理する必要があります。

// こうするべきではありません!エイリアスを作成する際に undefined を含めないでください!
type CoffeeResponse = Latte | Americano | undefined

class CoffeeService {
  getLatte(): CoffeeResponse {}
}

正しい方法:

// こうするべきです!エイリアスを使用する際に undefined をユニオンします!
type CoffeeResponse = Latte | Americano

class CoffeeService {
  // コードは空の値が発生する位置の近くでそれを処理する必要があります
  getLatte(): CoffeeResponse | undefined {}
}
  1. オプションのパラメータ / オプションのフィールドを優先する

TypeScript はオプションのパラメータとオプションのフィールドを作成することをサポートしています。たとえば:

interface CoffeeOrder {
  sugarCubes: number
  milk?: Whole | LowFat | HalfHalf
}

function pourCoffee(volume?: Milliliter) {}

オプションのパラメータは実際には型に |undefined を暗黙的にユニオンします。オプションフィールド(クラスまたはインターフェースに対して)とオプションのパラメータを使用し、|undefined 型のユニオンを使用しないようにします。

interface か type か?#

  1. interface:インターフェースは TypeScript によってオブジェクト型を定義するために設計されており、オブジェクトの形状を記述できます。
  2. type:型エイリアスはさまざまな型に別名を付けるために使用され、型そのものではなく、単なる別名です。

共通点:

  1. どちらもオブジェクトまたは関数を記述できます。
// interface
interface User {
  name: string
  age: number
}

interface SetUser {
  (name: string, age: number): void
}

// type
type User = {
  name: string
  age: number
}

type SetUser = (name: string, age: number) => void
  1. どちらも継承を許可します。

interface と type はどちらも継承でき、互いに独立しているわけではありません。つまり、interface は type を extends でき、type も interface を extends できます。効果はほぼ同じですが、文法は異なります。

// interface extends interface
interface Name {
  name: string
}
interface User extends Name {
  age: number
}

// type extends type
type Name = {
  name: string
}
type User = Name & { age: number }

// interface extends type
type Name = {
  name: string
}
interface User extends Name {
  age: number
}

// type extends interface
interface Name {
  name: string
}
type User = Name & {
  age: number
}

異なる点:

  1. type は基本型エイリアス、ユニオン型、交差型、タプルなどの型を宣言できますが、interface ではできません。
// 基本型エイリアス
type Name = string

// ユニオン型
interface Dog {
  wong()
}
interface Cat {
  miao()
}
type Pet = Dog | Cat

// タプル型、配列の各位置の型を具体的に定義
type PetList = [Dog, Pet]
  1. type 文では typeof を使用してインスタンスの型を取得して代入できます。
// 変数の型を取得したいときは typeof を使用します
const div = document.createElement('div')
type B = typeof div
  1. interface はマージを宣言でき、重複した宣言を行うと type はエラーになります。
interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User インターフェースは {
  name: string
  age: number
  sex: string
}
*/

まとめ:

  • ユニオン型、交差型、タプルなどの型を使用する場合は、type 型エイリアスを使用します。
  • 継承のために extends を使用する必要がある場合は、interface を使用します。
  • その他の型定義には interface を使用し、優先的に interface を使用します。

したがって、オブジェクトの型を宣言する必要がある場合は、オブジェクトリテラル式の型エイリアスではなく、インターフェースを使用するべきです:

// こうするべきです!
interface User {
  firstName: string
  lastName: string
}

// こうするべきではありません!
type User = {
  firstName: string
  lastName: string
}

なぜ?これらの 2 つの形式はほぼ同等であるため、2 つの形式のうちの 1 つを選択してプロジェクト内にバリエーションが発生しないようにするという原則に基づき、ここではより一般的なインターフェース形式を選択しました。関連技術的理由 TypeScript: Prefer Interfaces

TypeScript チームのリーダーの言葉:「正直なところ、私の個人的な意見は、モデル化できるオブジェクトにはすべてインターフェースを使用すべきだということです。対照的に、型エイリアスを使用することには何の利点もなく、特に型エイリアスには多くの表示上およびパフォーマンス上の問題があります」。

型検査を回避する#

  1. ダックタイプ

鳥がアヒルのように歩き、アヒルのように泳ぎ、アヒルのように鳴くのを見たら、その鳥はアヒルと呼ばれることができます。

ダックタイプは TypeScript において、私たちが鳥に歩く、泳ぐ、鳴くなどのメソッドを構築し、アヒルのような鳥を作成してアヒルの型検査を回避することを意味します。

interface Param {
  field1: string
}

const func = (param: Param) => param
func({ field1: '111', field2: 2 }) // エラー

const param1 = { field1: '111', field2: 2 }
func(param1) // 成功

ここでは、関数 func が Param 型の引数を受け取るように構築されており、直接 func を呼び出して引数を渡すと、変数 param に厳密に引数の検証が行われるため、エラーが発生します。

しかし、一時変数を使用して保存し、その変数を func に渡すと、ダックタイプの特性が適用されます。param1 には field1 が含まれているため、TypeScript は param1 が Param を完全に実装していると見なし、param1 の型は Param のサブクラスであると見なされるため、余分な field2 の検査を回避できます。

  1. 型アサーション
interface Param {
  field1: string
}

const func = (param: Param) => param
func({ field1: '111', field2: 2 } as Param) // 成功

any 型#

TypeScript の any 型はすべての他の型のスーパークラスであり、すべての他の型のサブクラスでもあり、すべてのプロパティを解参照することを許可します。したがって、any を使用することは非常に危険であり、重大なプログラムエラーを隠蔽し、対応する値が「静的プロパティを持つ」という原則を根本的に破壊します。

可能な限り any を使用しないでくださいany を使用する必要がある場合は、次の解決策を検討してください:

  1. any の影響範囲を縮小する
function f1() {
  const x: any = expressionReturningFoo() // 推奨されません。以降の x はすべて any になります
  processBar(x)
}

function f2() {
  const x = expressionReturningFoo()
  processBar(x as any) // 推奨されます。ここだけが any になります
}
  1. より詳細な any を使用する
const numArgsBad = (...args: any) => args.length // Return any は推奨されません
const numArgs = (...args: any[]) => args.length // Return number は推奨されます
  1. any の自動推論

TypeScript の any は一成不変ではなく、ユーザーの操作に応じて、TypeScript はより合理的な型を推測します。

const output = [] // any[]
output.push(1) // number[]
output.push('2') // (number|string)[]
  1. any よりも unknown を優先する

any 型の値は他の任意の型に代入でき、任意のプロパティを解参照できます。一般的に、この動作は必要ではなく、期待に反することが多いです。この場合、コードが表現しようとしている内容は「その型は未知である」ということです。このような場合は、組み込みの unknown 型を使用します。これは同じ意味を表現でき、unknown は任意のプロパティを解参照できないため、any よりも安全です。unknown 型の変数は、再び任意の他の型に再代入できます。

型アサーション#

  1. 型アサーションと非空アサーションを慎重に使用する

型アサーション(x as SomeType)と非空アサーション(y!)は安全ではありません。これらの構文はコンパイラを回避するだけであり、実行時のアサーションチェックを追加することはないため、プログラムが実行時にクラッシュする可能性があります。したがって、明確な理由がない限り、型アサーションと非空アサーションを使用すべきではありません

// こうするべきではありません!
;(x as Foo).foo()

y!.bar()

型と非空条件に対してアサーションを行いたい場合、最良の方法は実行時チェックを明示的に記述することです。

// こうするべきです!
// ここでは Foo がクラスであると仮定します。
if (x instanceof Foo) {
  x.foo()
}

if (y) {
  y.bar()
}

時には、コードのコンテキストに基づいて、あるアサーションが必然的に安全であることを確認できます。この場合、なぜこの不安全な動作が受け入れられるのかを詳細に説明するコメントを追加すべきです。アサーションの理由が明確であれば、コメントは必要ありません。

// こうするべきです!
// x は Foo 型の例です。なぜなら……
;(x as Foo).foo()

// y は null ではない可能性があります。なぜなら……
y!.bar()
  1. 型アサーションは as 構文を使用する必要があり、尖括弧構文を使用しないでください。これにより、アサーション外で括弧を使用することが強制されます。
// こうするべきではありません!
const x = (<Foo>z).length
const y = <Foo>z.length

// こうするべきです!
const x = (z as Foo).length
  1. オブジェクトリテラルの型を示すために型マーク(: Foo)を使用し、型アサーション(as Foo)を使用しないでください。将来的にインターフェースのフィールド型を変更する際、前者はプログラマーがバグを発見するのに役立ちます。
interface Foo {
  bar: number
  baz?: string // このフィールドの以前の名前は「bam」で、後に「baz」に改名されました。
}

const a: Foo = {
  bar: 123,
  bam: 'abc', // 型マークを使用している場合、改名後ここでエラーになります!
}

const b = {
  bar: 123,
  bam: 'abc', // 型アサーションを使用している場合、改名後ここでエラーにはなりません!
} as Foo

列挙型#

定数の集合を設定するためにオブジェクトの代わりに列挙型を使用します。オブジェクトで定義された通常の定数集合は、as const 修飾子を使用しない限り、変更時にエラーを提示しません。

// 悪い例
const Status = {
  Success: 'success',
}

// 良い例
enum Status {
  Success = 'success',
}

const enum を使用して定数列挙を宣言することもできます:

const enum Status {
  Success = 'success',
}

定数列挙と通常の列挙の違いは、主にアクセス性とコンパイル産物にあります。定数列挙では、列挙メンバーを通じてのみ列挙値にアクセスでき(値を通じてメンバーにアクセスすることはできません)、コンパイル産物には追加の補助オブジェクトが存在しません。列挙メンバーへのアクセスは、直接列挙の値にインライン置換されます。

列挙型には enum キーワードを使用する必要がありますが、const enum(定数列挙)は使用しないでください。TypeScript の列挙型自体は不変です

拡張:as const 修飾子が変数宣言や式の型に使用されると、TypeScript は変数や式の型を不変(immutable)として扱うことを強制します。これは、変数や式を変更しようとすると、TypeScript がエラーを報告することを意味します。

const foo = ['a', 'b'] as const
foo.push('c') // エラー、foo の型は不変として宣言されています

const bar = { x: 1, y: 2 } as const
bar.x = 3 // エラー、bar の型は不変として宣言されています

配列#

  • 単純型には配列の構文糖 T[] を使用すべきです。
  • その他の複雑な型には、より長い Array<T> を使用すべきです。

このルールは readonly T[]ReadonlyArray<T> にも適用されます。

// こうするべきです!
const a: string[]
const b: readonly string[]
const c: ns.MyObj[]
const d: Array<string | number>
const e: ReadonlyArray<string | number>

// こうするべきではありません!
const f: Array<string> // 構文糖の方が短い
const g: ReadonlyArray<string>
const h: { n: number; s: string }[] // 中括弧と中括弧があるため、この行は読みづらい
const i: (string | number)[]
const j: readonly (string | number)[]

関数#

  1. 戻り値が無視されるコールバック関数に any 型の戻り値タイプを設定しないでください。void を使用できます:
// 悪い例
function fn(x: () => any) {
  x()
}

// 良い例
function fn(x: () => void) {
  x()
}

void を使用することは比較的安全であり、x の戻り値を誤って使用することを防ぎます:

function fn(x: () => void) {
  const k = x() // おっと!他のことをするつもりでした
  k.doSomething() // エラー、しかし戻り値の型が 'any' であれば問題ありません
}
  1. 関数のオーバーロードは順序を付け、具体的なものをあいまいなものの前に配置するべきです。TypeScript は最初に一致したオーバーロードを選択するため、前のオーバーロードが後のものよりも「あいまい」である場合、後のものは隠され、選択されません:
// 悪い例
declare function fn(x: any): any
declare function fn(x: HTMLElement): number
declare function fn(x: HTMLDivElement): string

let myElem: HTMLDivElement
let x = fn(myElem) // x: any, 何だこれ?

// 良い例
declare function fn(x: HTMLDivElement): string
declare function fn(x: HTMLElement): number
declare function fn(x: any): any

let myElem: HTMLDivElement
let x = fn(myElem) // x: string, :)
  1. オプションのパラメータを優先し、オーバーロードを定義しないでください:
// 悪い例
interface Example {
  diff(one: string): number
  diff(one: string, two: string): number
  diff(one: string, two: string, three: boolean): number
}

// 良い例
interface Example {
  diff(one: string, two?: string, three?: boolean): number
}
  1. ユニオン型を使用し、特定の位置でのみパラメータ型が異なる場合にオーバーロードを定義しないでください:
// 悪い例
interface Moment {
  utcOffset(): number
  utcOffset(b: number): Moment
  utcOffset(b: string): Moment
}

// 良い例
interface Moment {
  utcOffset(): number
  utcOffset(b: number | string): Moment
}

クラス#

  1. #private 構文を使用しない

#private プライベートフィールド(プライベート識別子とも呼ばれる)構文を使用してプライベートメンバーを宣言しないでください。TypeScript のアクセス修飾子を使用するべきです。

// こうするべきではありません!
class Clazz {
  #ident = 1
}

// 良い例
class Clazz {
  private ident = 1
}

なぜ?プライベートフィールド構文は、TypeScript が JavaScript にコンパイルされる際にサイズとパフォーマンスの問題を引き起こします。また、ES2015 以前の標準はプライベートフィールド構文をサポートしていないため、TypeScript の最低コンパイルターゲットは ES2015 に制限されます。さらに、静的型と可視性のチェックを行う際、プライベートフィールド構文はアクセス修飾子に対して明確な利点を持ちません。

  1. readonly を使用する

コンストラクタ以外で値が設定されないプロパティには、readonly 修飾子を使用してマークします。これらのプロパティは深い不変性を持つ必要はありません。

  1. パラメータプロパティ

コンストラクタ内でクラスメンバーを明示的に初期化しないでください。TypeScript のパラメータプロパティ構文を使用するべきです。コンストラクタのパラメータの前に修飾子や readonly を追加することは、そのプロパティをクラス内で定義し、そのプロパティに値を設定することと同等であり、コードをより簡潔にします。

// こうするべきではありません!重複したコードが多すぎます!
class Foo {
  private readonly barService: BarService
  constructor(barService: BarService) {
    this.barService = barService
  }
}

// 良い例
class Foo {
  constructor(private readonly barService: BarService) {}
}
  1. フィールド初期化

メンバーがパラメータプロパティでない場合、宣言時に初期化するべきです。これにより、時にはコンストラクタを完全に省略できます。

// こうするべきではありません!初期化文をコンストラクタに単独で置く必要はありません!
class Foo {
  private readonly userList: string[]
  constructor() {
    this.userList = []
  }
}

// 良い例
class Foo {
  private readonly userList: string[] = []
}
  1. サブクラスが親クラスを継承する際、親クラスのメソッドをオーバーライドする必要がある場合は、override 修飾子を追加する必要があります。
class Animal {
  eat() {
    console.log('food')
  }
}

// 悪い例
class Dog extends Animal {
  eat() {
    console.log('bone')
  }
}

// 良い例
class Dog extends Animal {
  override eat() {
    console.log('bone')
  }
}

スタイル#

  1. 匿名関数式の代わりにアロー関数を使用します。
// 良い例
bar(() => {
  this.doSomething()
})

// 悪い例
bar(function () {})
  1. アロー関数の引数を括弧で囲む必要がある場合のみ囲みます。たとえば、(x) => x + x は誤りで、以下が正しい方法です:
  2. x => x + x
  3. (x,y) => x + y
  4. <T>(x: T, y: T) => x === y
  5. ループ体と条件文を常に {} で囲みます。
  6. 小括弧内に空白を入れないでください。コンマ、コロン、セミコロンの後には空白を 1 つ入れます。たとえば:
  7. for (let i = 0, n = str.length; i < 10; i++) {}
  8. if (x < 10) {}
  9. function f(x: number, y: string): void {}
  10. 各変数宣言文では 1 つの変数のみを宣言します(たとえば let x = 1; let y = 2; とし、let x = 1, y = 2; は使用しません)。
  11. 関数が戻り値を持たない場合、void を使用するのが最善です。
  12. 等価性の判断には必ず三等号(===)と対応する不等号(!==)を使用してください。二等号は比較の過程で型変換を行うため、理解しにくいエラーを引き起こすことが非常に容易です。また、JavaScript 仮想マシン上では、二等号の実行速度は三等号よりも遅くなります。JavaScript 等価表

参考資料#

  1. コーディングガイドライン
  2. TypeScript マニュアル
  3. TypeScript 中国語マニュアル
  4. Google TypeScript スタイルガイド
  5. インターフェースに "I" をプレフィックスすることの禁止
  6. TypeScript のインターフェースとクラスのコーディングガイドラインについて混乱している
  7. Typescript 開発規範
  8. TypeScript における as const とは
  9. TypeScript: Prefer Interfaces
  10. TypeScript における interface と type の違いは何か?
  11. Typescript 宣言ファイル - 第三者の型拡張
  12. Effective Typescript:TypeScript を使用するための n のテクニック
  13. JavaScript 等価表
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。