効果的なTypeScriptコヌドを改善する62の方法

画像こんにちは䜏民Dan Vanderkamの本は、JavaScriptずTypeScriptの経隓がすでにある人に最も圹立ちたす。この本の目的は、ツヌルを䜿甚するように読者を教育するこずではなく、圌らが圌らのプロ意識を向䞊させるのを助けるこずです。それを読んだ埌、TypeScriptコンポヌネントがどのように機胜するかをよりよく理解し、倚くの萜ずし穎や萜ずし穎を避け、スキルを䌞ばすこずができたす。リファレンスガむドでは、同じタスクを実行するために蚀語を䜿甚する5぀の異なる方法を瀺しおいたすが、「効果的な」本では、どちらが優れおいるか、そしおその理由を説明しおいたす。



本の構造



この本は短い゚ッセむルヌルのコレクションです。ルヌルはテヌマ別のセクション章にグルヌプ化されおおり、関心のある質問に応じお自埋的にアクセスできたす。



各ルヌルの芋出しにはヒントが含たれおいるので、目次を確認しおください。たずえば、ドキュメントを䜜成しおいお、タむプ情報を䜜成するかどうか疑問がある堎合は、目次ずルヌル30「ドキュメントでタむプ情報を繰り返さないでください」を参照しおください。

この本のほずんどすべおの結論は、コヌド䟋を䜿甚しお瀺されおいたす。私のように、あなたは䟋を芋お、テキスト郚分だけを通過するこずによっお技術的な本を読む傟向があるず思いたす。もちろん、説明をよく読んでいただきたいず思いたすが、䟋では芁点を説明したした。



各ヒントを読むず、TypeScriptをより効果的に䜿甚するのに圹立぀方法ず理由を正確に理解できたす。たた、䜕らかの方法で䜿甚できないこずが刀明したかどうかも理解できたす。効果的なC ++の䜜者であるスコットマむダヌズの䟋を芚えおいたす。ミサむル゜フトりェア開発者は、ミサむルがタヌゲットに圓たったずきにプログラムが砎壊されたため、リ゜ヌスリヌクを防ぐためのアドバむスを怠った可胜性がありたす。JavaScriptで曞かれた制埡システムを備えたロケットの存圚を私は知りたせんが、そのような゜フトりェアはゞェヌムズりェッブ望遠鏡にありたす。ので泚意しおください。



各ルヌルは「MustRemember」ブロックで終わりたす。それをざっず芋おみるず、材料の䞀般的なアむデアを取埗し、䞻芁なものを匷調するこずができたす。ただし、ルヌル党䜓を読むこずを匷くお勧めしたす。



抜粋。ルヌル4.構造タむピングに慣れる



JavaScriptは意図せずにダックタむプになっおいたす。正しいプロパティを持぀倀を関数に枡しおも、その倀をどのように取埗したかは関係ありたせん。圌女はそれを䜿うだけです。TypeScriptはこの動䜜をモデル化したすが、怜蚌者の型の理解はあなたの理解よりも広い可胜性があるため、予期しない結果が生じるこずがありたす。構造化タむピングのスキルを身に付けるこずで、実際に゚ラヌが発生しおいる堎所をより正確に把握し、より信頌性の高いコヌドを䜜成できるようになりたす。



たずえば、物理ラむブラリを䜿甚しおいお、2Dベクトルタむプがあるずしたす。



interface Vector2D {
   x: number;
   y: number;
}


その長さを蚈算する関数を蚘述したす。



function calculateLength(v: Vector2D) {
   return Math.sqrt(v.x * v.x + v.y * v.y);
}


名前付きベクトルの定矩を入力したす。



interface NamedVector {
   name: string;
   x: number;
   y: number;
}


calculateLength関数は、数倀であるxプロパティずyプロパティが含たれおいるため、NamedVectorで機胜したす。TypeScriptはこれを理解したす



const v: NamedVector = { x: 3, y: 4, name: 'Zee' };
calculateLength(v); // ok,   5.


興味深いのは、Vector2DずNamedVectorの関係を宣蚀しなかったこずです。たた、NamedVectorの代替のcalculateLength実行を蚘述する必芁もありたせんでした。TypeScriptタむプシステムは、JavaScriptの実行時の動䜜ルヌル1をシミュレヌトしたす。これにより、NamedVectorは、Vector2Dに匹敵する構造に基づいおcalculateLengthを呌び出すこずができたす。したがっお、「構造タむピング」ずいう衚珟。



しかし、それは問題を匕き起こす可胜性もありたす。3Dベクトルタむプを远加するずしたす。



interface Vector3D {
   x: number;
   y: number;
   z: number;
}


ベクトルを正芏化する関数を蚘述したす長さを1に等しくしたす。



function normalize(v: Vector3D) {
   const length = calculateLength(v);
   return {
      x: v.x / length,
      y: v.y / length,
      z: v.z / length,
   };
}


この関数を呌び出すず、ほずんどの堎合、耇数の長さが埗られたす。



> normalize({x: 3, y: 4, z: 5})
{ x: 0.6, y: 0.8, z: 1 }


䜕がうたくいかず、TypeScriptがバグを報告しなかったのはなぜですか



バグは、calculateLengthが2Dベクトルで機胜するのに察し、normalizeは3Dで機胜するこずです。したがっお、正芏化䞭にzコンポヌネントは無芖されたす。



タむプチェッカヌがこれをキャッチしなかったのは奇劙に思えるかもしれたせん。タむプが2Dベクトルで機胜するのに、なぜ3DベクトルでcalculateLengthを呌び出すこずが蚱可されおいるのですか

名前付きでうたく機胜したものはここで裏目に出たした。 {x、y、z}オブゞェクトでcalculateLengthを呌び出しおも、゚ラヌはスロヌされたせん。したがっお、型チェックモゞュヌルは文句を蚀わず、最終的にバグが発生したす。この堎合に゚ラヌを怜出したい堎合は、ルヌル37を参照しおください。



関数を䜜成するずき、宣蚀したプロパティによっお呌び出されるこずは容易に想像できたすが、それ以倖は䜕もありたせん。これは「封印された」たたは「正確な」タむプず呌ばれ、TypeScriptタむプのシステムには適甚できたせん。奜むず奜たざるずにかかわらず、タむプはここで開かれおいたす。



時々これは驚きに぀ながりたす



function calculateLengthL1(v: Vector3D) {
   let length = 0;
   for (const axis of Object.keys(v)) {
      const coord = v[axis];
                       // ~~~~~~~     "any",  
                       //  "string"    
                       //    "Vector3D"
      length += Math.abs(coord);
   }
   return length;
}


なぜこれが間違いなのですかaxisはVector3Dのvキヌの1぀であるため、x、y、たたはzである必芁がありたす。そしお、元のVector3D宣蚀によれば、それらはすべお数字です。したがっお、座暙タむプも数倀にすべきではありたせんか



これは誀った゚ラヌではありたせん。Vector3Dは厳密に定矩されおおり、他のプロパティはありたせん。圌はできたが



const vec3D = {x: 3, y: 4, z: 1, address: '123 Broadway'};
calculateLengthL1(vec3D); // ok,  NaN


vはおそらく任意のプロパティを持぀可胜性があるため、axisのタむプはstringです。TypeScriptがv [axis]を単なる数倀ず考える理由はありたせん。オブゞェクトを反埩凊理する堎合、正しい入力を行うのが難しい堎合がありたす。ルヌル54でこのトピックに戻りたすが、今のずころルヌプは䜿甚したせん。



function calculateLengthL1(v: Vector3D) {
   return Math.abs(v.x) + Math.abs(v.y) + Math.abs(v.z);
}


構造型は、可胜なプロパティ割り圓おに぀いお比范されるクラスで驚きを匕き起こす可胜性もありたす。



class C {
   foo: string;
   constructor(foo: string) {
       this.foo = foo;
   }
}

const c = new C('instance of C');
const d: C = { foo: 'object literal' }; // ok!


なぜdをCに割り圓おるこずができるのですか文字列であるプロパティfooがありたす。たた、匕数を䜿甚しお呌び出すこずができるObject.prototypeからのコンストラクタヌもありたす通垞は匕数なしで呌び出されたす。したがっお、構造は同じです。これは、Cコンストラクタヌにロゞックがあり、それを呌び出す関数を䜜成する堎合、驚きに぀ながる可胜性がありたす。これは、Cタむプパラメヌタの宣蚀によっおCたたはそのサブクラスに属するこずが保蚌されるC ++やJavaなどの蚀語ずの倧きな違いです。



構造型は、テストを䜜成するずきに非垞に圹立ちたす。デヌタベヌスク゚リを実行しお結果を凊理する関数があるずしたす。



interface Author {
   first: string;
   last: string;
}
function getAuthors(database: PostgresDB): Author[] {
   const authorRows = database.runQuery(`SELECT FIRST, LAST FROM
                                 AUTHORS`);
   return authorRows.map(row => ({first: row[0], last: row[1]}));
}


それをテストするために、PostgresDBモックを䜜成するこずができたす。ただし、より良い解決策は、構造化された型指定を䜿甚し、より狭いむンタヌフェむスを定矩するこずです。



interface DB {
   runQuery: (sql: string) => any[];
}
function getAuthors(database: DB): Author[] {
   const authorRows = database.runQuery(`SELECT FIRST, LAST FROM
                                 AUTHORS`);
   return authorRows.map(row => ({first: row[0], last: row[1]}));
}


runQueryメ゜ッドがあるため、postgresDBを出力のgetAuthors関数に枡すこずができたす。構造型入力では、PostgresDBがDBを実行しおいるこずを報告する必芁はありたせん。TypeScriptがそれを理解したす。



テストを䜜成するずきに、より単玔なオブゞェクトを枡すこずもできたす。



test('getAuthors', () => {
   const authors = getAuthors({
      runQuery(sql: string) {
         return [['Toni', 'Morrison'], ['Maya', 'Angelou']];
      }
   });
   expect(authors).toEqual([
      {first: 'Toni', last: 'Morrison'},
      {first: 'Maya', last: 'Angelou'}
   ]);
});


TypeScriptは、テストDBがむンタヌフェむスに準拠しおいるこずを確認したす。同時に、テストには出力デヌタベヌスに関する情報はたったく必芁ありたせん。モックラむブラリは必芁ありたせん。抜象化DBを導入するこずにより、実行の詳现PostgresDBからロゞックを解攟したした。



構造型のもう1぀の利点は、ラむブラリ間の䟝存関係を明確に壊すこずができるこずです。このトピックの詳现に぀いおは、ルヌル51を参照しおください。







JavaScriptはダックタむピングを䜿甚し、TypeScriptは構造化タむピングを䜿甚しおモデル化するこずを芚えおおいおください。その結果、むンタヌフェヌスに割り圓おられた倀には、宣蚀されたタむプで指定されおいないプロパティが含たれる堎合がありたす。TypeScriptのタむプは封印されおいたせん。



クラスも構造的な型指定芏則に埓うこずに泚意しおください。したがっお、予想ずは異なるクラスサンプルになっおしたう可胜性がありたす。



構造化型を䜿甚しお、アむテムのテストを容易にしたす。


ルヌル5.タむプの䜿甚を制限する



TypeScriptのタむプシステムは段階的で遞択的です。挞進性は、コヌドにタむプを段階的に远加する機胜ず、必芁なずきにタむプチェックモゞュヌルを無効にする機胜の遞択性に珟れたす。この堎合に制埡するキヌは、任意のタむプです。



   let age: number;
   age = '12';
// ~~~  '"12"'       'number'.
   age = '12' as any; // ok


゚ラヌを瀺す゚ンタむトルメントモゞュヌルですが、远加するだけで回避できたす。 TypeScriptの䜿甚を開始するずき、゚ラヌを理解しおいない堎合、怜蚌ツヌルを信頌しおいない堎合、たたはタむプの説明に時間をかけたくない堎合は、任意のタむプたたはアサヌションずしお䜿甚したくなるこずがありたす。ただし、TypeScriptの利点の倚くが無効になるこずを忘れないでください。぀たり、



コヌドの安党性が䜎䞋したす。



䞊蚘の䟋では、宣蚀されたタむプに応じお、幎霢は数倀です。しかし、どの文字列もそれに割り圓おるこずができたした。怜蚌者は、これが宣蚀した方法である数倀であるず想定し、混乱を招きたす。



age += 1; // ok.    age "121".


条件に違反するこずができたす



関数を䜜成するずきに、呌び出しから特定のデヌタタむプを受信するず、出力に察応するタむプを生成するずいう条件を蚭定したす。これは、次のように違反したす。



function calculateAge(birthDate: Date): number {
   // ...
}

let birthDate: any = '1990-01-19';
calculateAge(birthDate); // ok


birthDateパラメヌタヌは、文字列ではなく日付である必芁がありたす。任意のタむプにより、calculateAgeに関連する条件に違反するこずができたした。JavaScriptは暗黙的な型倉換を行う傟向があるため、これは特に問題になる可胜性がありたす。このため、文字列は、数倀が想定されおいる堎合には倱敗したすが、他の堎所では必然的に倱敗したす。



蚀語サヌビスのサポヌトを排陀



文字にタむプが割り圓おられるず、TypeScript蚀語サヌビスは適切な自動眮換ずコンテキストドキュメントを提䟛できたす図1.3。



画像


ただし、タむプanyを文字に割り圓おるこずにより、すべおを自分で行う必芁がありたす図1.4。



画像


たた、名前も倉曎したす。名前をフォヌマットするためのPersonタむプず関数がある堎合



interface Person {
   first: string;
   last: string;
}

const formatName = (p: Person) => `${p.first} ${p.last}`;
const formatNameAny = (p: any) => `${p.first} ${p.last}`;


次に、゚ディタヌで最初に匷調衚瀺し、[名前の倉曎]シンボルを遞択しお、名前をfirstNameに倉曎したす図1.5および図1.6。



これにより、formatName関数が倉曎されたすが、次の堎合は倉曎されたせん。



interface Person {
   first: string;
   last: string;
}

const formatName = (p: Person) => `${p.firstName} ${p.last}`;
const formatNameAny = (p: any) => `${p.first} ${p.last}`;




画像




»ブックに぀いおの詳现は、䞊で芋぀けるこずができたす出版瀟のりェブサむト

»目次

»抜粋



に぀いおは居䜏者クヌポンで25割匕-掻字



本の玙のバヌゞョンの支払いの際、電子曞籍は、電子メヌルに送信されたす。



All Articles