Java/C# プログラマー向けの TypeScript

TypeScript は、C# や Java などの静的型付けを持つ他の言語に慣れているプログラマーに人気の選択肢です。

TypeScript の型システムは、より優れたコード補完、エラーの早期検出、プログラムのパーツ間のより明確なコミュニケーションなど、多くの同じ利点を提供します。TypeScript はこれらの開発者にとって多くの馴染みのある機能を提供しますが、JavaScript (ひいては TypeScript) が従来の OOP 言語とどのように異なるかを確認することは価値があります。これらの違いを理解することは、より良い JavaScript コードを記述し、C#/Java から TypeScript に直接移行するプログラマーが陥りがちな一般的な落とし穴を避けるのに役立ちます。

JavaScriptの並行学習

すでに JavaScript に精通しているが、主に Java や C# のプログラマーである場合、この入門ページは、陥りやすい一般的な誤解や落とし穴を説明するのに役立ちます。TypeScript が型をモデル化する方法は、Java や C# とは大きく異なる点があり、TypeScript を学習する際には、これらの点を念頭に置いておくことが重要です。

Java や C# のプログラマーで、JavaScript全般が初めての場合は、まず型なしで JavaScript を少し学習して、JavaScript のランタイム動作を理解することをお勧めします。TypeScript はコードの実行方法を変更しないため、実際に何かを行うコードを記述するには、JavaScript の仕組みを学習する必要があります。

TypeScript は JavaScript と同じランタイムを使用しているため、特定のランタイム動作 (文字列を数値に変換する、アラートを表示する、ディスクにファイルを書き込むなど) を実行する方法に関するリソースは、TypeScript プログラムにも同様に適用されることに注意してください。TypeScript 特有のリソースに限定しないでください。

クラスの再考

C# と Java は、いわゆる必須 OOP 言語です。これらの言語では、クラスはコード編成の基本単位であり、ランタイムにおけるすべてのデータ動作の基本的なコンテナでもあります。すべての機能とデータをクラスに保持することを強制することは、一部の問題にとっては良いドメインモデルになる可能性がありますが、すべてのドメインをこのように表現する必要はありません。

自由な関数とデータ

JavaScriptでは、関数はどこにでも配置でき、データは事前に定義されたclassstruct内に存在することなく自由に受け渡すことができます。この柔軟性は非常に強力です。OOP階層を前提としない「フリー」関数(クラスに関連付けられていない関数)がデータを処理するという方法は、JavaScriptでプログラムを書く際の好ましいモデルです。

...静的クラス

さらに、シングルトンや静的クラスなど、C#やJavaにある特定の構成要素はTypeScriptでは不要です。

...TypeScriptにおけるOOP

とはいえ、必要であればクラスを使用することもできます。従来のOOP階層によってうまく解決できる問題もあり、TypeScriptがJavaScriptのクラスをサポートしていることで、これらのモデルはさらに強力になります。TypeScriptは、インターフェースの実装、継承、静的メソッドなど、多くの一般的なパターンをサポートしています。

クラスについては、このガイドの後半で説明します。

...型の再考

TypeScriptにおける*型*の理解は、実際にはC#やJavaとは大きく異なります。いくつかの違いを見ていきましょう。

...名義的実体化型システム

C#やJavaでは、任意の値またはオブジェクトは、null、プリミティブ、または既知のクラスタイプのいずれかである正確な型を1つ持ちます。実行時に正確な型を照会するために、value.GetType()value.getClass()などのメソッドを呼び出すことができます。この型の定義は、どこかのクラスに名前とともに存在し、明示的な継承関係または共通して実装されたインターフェースがない限り、形状が似ている2つのクラスを互いに代わりに使用することはできません。

これらの側面は、*実体化された、名義的な*型システムを表しています。コードに記述された型は実行時に存在し、型は構造ではなく宣言によって関連付けられます。

...集合としての型

C#またはJavaでは、実行時型とそのコンパイル時宣言間の1対1の対応を考えることは意味があります。

TypeScriptでは、型を共通点を持つ*値の集合*と考える方が適切です。型は単なる集合であるため、特定の値は同時に*複数*の集合に属することができます。

型を集合として考え始めると、特定の操作が非常に自然になります。たとえば、C#では、*string*または*int*のいずれかである値を渡すのは厄介です。なぜなら、この種の値を表す単一の型が存在しないからです。

TypeScriptでは、すべての型が単なる集合であることに気付くと、これは非常に自然になります。*string*集合または*number*集合のいずれかに属する値をどのように記述しますか?それは単にそれらの集合の*和集合*に属します:string | number

TypeScriptは、集合論的な方法で型を操作するための多くのメカニズムを提供しており、型を集合として考えると、より直感的に理解できるでしょう。

...消去構造型システム

TypeScriptでは、オブジェクトは単一の正確な型ではありません。たとえば、インターフェースを満たすオブジェクトを構築する場合、2つの間に宣言的な関係がなかったとしても、そのインターフェースが期待される場所でそのオブジェクトを使用できます。

ts
interface Pointlike {
x: number;
y: number;
}
interface Named {
name: string;
}
 
function logPoint(point: Pointlike) {
console.log("x = " + point.x + ", y = " + point.y);
}
 
function logName(x: Named) {
console.log("Hello, " + x.name);
}
 
const obj = {
x: 0,
y: 0,
name: "Origin",
};
 
logPoint(obj);
logName(obj);
Try

TypeScriptの型システムは、名義的ではなく*構造的*です。objは、両方とも数値であるxおよびyプロパティを持っているため、Pointlikeとして使用できます。型間の関係は、特定の関係で宣言されたかどうかではなく、含まれているプロパティによって決定されます。

TypeScriptの型システムは*実体化されていません*。実行時にobjPointlikeであることを示すものは何もありません。実際、Pointlike型は実行時には*いかなる形式でも*存在しません。

*集合としての型*という考えに戻ると、objPointlike値の集合とNamed値の集合の両方のメンバーであると考えることができます。

...構造的タイピングの結果

OOPプログラマーは、構造的タイピングの2つの特定の側面にしばしば驚かされます。

...空の型

1つ目は、*空の型*が期待に反しているように見えることです。

ts
class Empty {}
 
function fn(arg: Empty) {
// do something?
}
 
// No error, but this isn't an 'Empty' ?
fn({ k: 10 });
Try

TypeScriptは、ここでfnの呼び出しが有効かどうかを、提供された引数が有効なEmptyであるかどうかを確認することで判断します。これは、{ k: 10 }と`class Empty { } `` *構造*を調べることによって行われます。`Empty`にはプロパティがないため、`{ k: 10 } `は`Empty`の*すべて*のプロパティを持っていることがわかります。したがって、これは有効な呼び出しです!

これは驚くべきことかもしれませんが、最終的には名義的なOOP言語で強制される関係と非常によく似ています。サブクラスは、基底クラスのプロパティを*削除*できません。そうすると、派生クラスとその基底間の自然なサブタイプ関係が破壊されるためです。構造的型システムは、互換性のある型のプロパティを持つという観点からサブタイプを記述することにより、この関係を暗黙的に識別します.

...同一の型

もう1つのよくある驚きの原因は、同一の型です。

ts
class Car {
drive() {
// hit the gas
}
}
class Golfer {
drive() {
// hit the ball far
}
}
// No error?
let w: Car = new Golfer();

繰り返しますが、これらのクラスの*構造*が同じであるため、これはエラーではありません。これは混乱の原因となる可能性があるように思われますが、実際には、関連付けられるべきではない同一のクラスは一般的ではありません。

クラスが互いにどのように関連しているかについては、「クラス」の章で詳しく学習します。

...リフレクション

OOPプログラマーは、ジェネリックな値であっても、あらゆる値の型を照会できることに慣れています。

csharp
// C#
static void LogType<T>() {
Console.WriteLine(typeof(T).Name);
}

TypeScriptの型システムは完全に消去されるため、たとえばジェネリック型パラメータのインスタンス化に関する情報は実行時には使用できません。

JavaScriptには、typeofinstanceofなど、限られたプリミティブがいくつかありますが、これらの演算子は型消去出力コードに存在する値に対して引き続き機能することを覚えておいてください。たとえば、typeof (new Car())"object"であり、Carまたは"Car"ではありません。

...次のステップ

これは、TypeScriptで日常的に使用される構文とツールの簡単な概要でした。ここから、次のことができます。

TypeScriptのドキュメントはオープンソースプロジェクトです。プルリクエストを送信して、これらのページの改善にご協力ください❤

このページの貢献者
OTOrta Therox (13)
SPSamuel Pak (1)
LLalit (1)
GDGonzalo Diethelm (1)

最終更新日:2024年3月21日