はじめに
TypeScriptの基本文法についてのメモ
参考
TypeScriptの特徴
- JavaScriptに静的型付けシステムとクラスベースのオブジェクト指向を加えた上位互換の言語
- 型チェック、型安全性: 間違ったプログラムをコンパイラが型チェックで検出してくれる
- 型推論: 型を書かなくても言語処理系が文脈から型を予想して型付けしてくれる
- Null安全性: コンパイルの段階でNullアクセスエラーになる可能性のあるコードをチェックして教えてくれるようになった
設定ファイルtsconfig.json
- TypeScriptコンパイラに対する設定を記述したファイル
- tscはtsconfig.jsonを自動的に読み込み、それに従ってコンパイルを実行
TypeScriptの文法
型アノテーション(型注釈)
- 型の不整合があると、コンパイル時にチェックされる
const 変数: 型 = 式;
比較演算子
==
は異なる型の比較した場合、暗黙の型変換をおこなってから両者を比較するためtrueになることがある。厳密に比較は===
を使う。
console.log(str === 3)
論理演算子
x && y
x || y
x ?? y
-
真偽値の場合
- &&は両辺が真のときのみ結果が真、||は片方が真のときに結果が真
- true 、falseを返す
-
真偽値以外の場合
-
左側のオペランドを真偽値に変換した結果、x、yを返す
-
x && y
- xを真偽値に変換した結果、falseならx、trueならyを返す
-
x ?? y
- xがnullまたはundefinedのときのみyを返す
- (||は0、falseなどの値もないものとして扱ってしまう)
- データがない場合は代替の値を使う、というシチュエーションに適している
-
オブジェクト
ここではクラスに由来しないオブジェクトのこと
// 省略記法
// プロパティと変数名が同じ場合
{name}
//動的なプロパティの決定
// インデックスシグネチャ
//[変数名: 型]
const obj = {
[name: string]: 123
}
インデックスシグネチャを使うと存在しないプロパティへのアクセスなどはコンパイルエラーにならず、型安全性が壊れてしまう。なので極力使わないほうがよさそう。
- オブジェクト型
const obj: {
foo: number,
bar: string
}
= {
foo: 1,
bar: "abc"
}
- type文で型に別名をつける
type FoobarObj ={
foo: number,
bar: string
}
const obj: FoobarObj
= {
foo: 1,
bar: "abc"
}
- typofキーワード
* 型なし変数の型推論を抽出して使うことができる。 - ただ、型は明示的に記載するべきなので、あまりtypeofを利用しないほうがよい
型引数
型を作成するときにパラメータを付与することができる
type USer<T>={
name: string,
child: T
}
このときThahasName型の部分型でなければいけない
type USer<T extends hasName>={
name: string,
child: T
}
const
この場合はnameや、ageは再代入できるが、userに別のオブジェクトを再代入はできないということ
const user ={
nam: "aaa",
age: 25,
}
typeとinterface
- typeは任意の型に対して別名をつけることができる、interfaceはオブジェクト型のみ
- typeのほうがよく使われるみたい
const user ={
nam: "aaa",
age: 25,
}
type T = typeof user;
const obj2: T = ...
typeof
型推論の結果を型として抽出、再利用する
部分型関係
ある型をほかの型の代わりに使える、ある型を他の型とみなせる
分割代入
- オブジェクトから値を取り出して、変数に代入する
const {foo,bar} = obj;
- これはオブジェクトのプロパティの中身をプロパティと同名の変数に入れたい場合にしか使えない。が、多くの場合同名を使う
ネストしたパターン
const nested = {
num: 123,
obj: {
foo: "heelo",
}
}
const {num, obj:{foo}} = nested
console.log(foo) // hello
Enum
Enumを使うより、リテラル型でEnumを表現するほうがよく利用されている
type Direction = "NORTH" | "SOUTH" | "EAST" | "WEST";
function move(direction: Direction) {
// ...
}
move("NORTH"); // OK
move("SOUTHWEST"); // エラー: Argument of type '"SOUTHWEST"' is not assignable to parameter of type 'Direction'.
タプル型
固定数の要素を持ち、それぞれの要素が異なる型を持つことができる配列のような型です。タプルは配列と似ていますが、要素の数とその型が固定されている点が異なります。
let x: [string, number];
x = ["hello", 10]; // OK
関数の型定義
TypeScriptではコンパイラオプションにnoImplicitAny
が指定されていないと、引数の型定義がなくても暗黙のうちにany
があてがわれてコンパイルが通ってしまうので、tsconfig.jsonにnoImplicityAnyの設定が必要
引数と戻り値をまとめて定義する方法
関数の型定義
interface Greeter {
(message: string, recipient: string): void;
}
const greet: Greeter = (msg, person) => {
console.log(`${msg}, ${person}!`);
};
Reactの関数コンポーネントの型
type TodoPresenterProps ={
todos: Todo[]
}
// <>はpropsの型
export const TodoPresenter: React.FC<TodoPresenterProps> = ({todos,}) => {
return (
<div>
</div>
)
}
ジェネリクスと型引数
型を具体的に指定せずにコンポーネントを定義し、それを使用する際に具体的な型を指定することができます
//<T>がジェネリクス
function identity<T>(arg: T): T {
return arg;
}
//<string>が型引数
let output = identity<string>("hello");
型推論で以下のようにもかける
let output = identity("hello"); // string型が推論される
型引数
TypeScriptのジェネリクスの一部として提供される機能であり、型をパラメータとして関数やクラス、インタフェースなどに渡すことができます。これにより、汎用的で再利用可能なコードを記述することができるようになります。
特定の型に依存せず、かつ型の安全性を保持したままコードを実装することができます
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("hello");
let output2 = identity<number>(123);
identity関数は、型引数Tを持っており、入力としてT型の値を受け取り、同じT型の値を返します。この関数を呼び出す際に、具体的な型(この場合はstringやnumber)を指定することで、関数の挙動をその型に合わせることができます。
ユニオン型とインターセクション型
ユニオン型
複数の型のうちのどれか一つの型を持つことができる構文
type StringOrNumber = string | number;
let value: StringOrNumber;
value = "hello"; // OK
value = 123; // OK
value = true; // Error: boolean型はStringOrNumber型に代入できない
インターセクション型
複数の型を組み合わせて、それらのすべての特性を持つ新しい型を作成するために使用
オブジェクト型を拡張した新しい型を作る
type Name = {
name: string;
};
type Age = {
age: number;
};
type Person = Name & Age;
let person: Person = {
name: "Alice",
age: 30
};
オプショナルチェイニングによるプロパティアクセス
obj?.propの場合、objがnull、undefinedの場合でもランタイムエラーは発生せず、結果はundefinedになる
メソッドの場合はobj?.method()。
要するに簡潔に書ける
リテラル型
文字、数値、真偽値、BigIntはリテラルをそのまま型として扱うことができる
const foo: "foo" = "foo";
const two: 2 = 2; //2型
const isTrue: true = true;
const bigIntValue: 123n = 123n;
//今までのように型指定してなくても、constは型推論でリテラル型にしていた
const name = "aaa" // aaa型。なので再代入不可
リテラル型のwidening
リテラル型が自動的に対応するプリミティブ型に変化する挙動
// これはfoo型
const foo = "foo";
// これはstring型。プリミティブ型に変換される
// letで宣言された変数はあとで再代入されることが期待されるから
let foo = "foo";
// オブジェクトもプロパティは再代入可能なので、プリミティブ型になる
型のNull安全性を保証する
- デフォルトの設定ではすべての型にnullとundefinedを代入できてしまう
tstrictNullChecksを設定して、nullやundfinedを他の方から区別する - このような型の厳格なチェックにより、null参照のエラーをコンパイル時に検出して、実行時の問題を減少させることができます
- nameにnullを代入するには、型をstring | nullのように明示的に指定する必要があります
typeof演算子
変数の型をキャプチャして、他の変数の型として再利用することができます
let person = {
name: "Alice",
age: 30
};
type PersonType = typeof person;
function greet(arg: PersonType) {
console.log(`Hello, my name is ${arg.name} and I am ${arg.age} years old.`);
}
typeofをオブジェクトに適用して、オブジェクトの型を取得できる。
const person = {
name: "Alice",
age: 30
};
type PersonType = typeof person; // type PersonType = { name: string; age: number; }
typeof
typeof 変数名で型を取り出す。型推論されている型を抽出、再利用など
keyof型
keyof 型 で型が例えばオブジェクトである場合、オブジェクトのプロパティ名全てを受け入れる型になる。A | B | C。
type Human ={
name: string,
age: number
}
// "name" | "age"という型になる
type Humankeys = keof Human;
keyof typeof オブジェクトとして、オブジェクトの型から各プロパティ名を型として抽出するときによく使われる。
型ガード、型絞り込み
型ガードは、TypeScriptのコード内である変数やオブジェクトの型を確認または絞り込むためのパターンや構造です。型ガードを使用することで、特定のスコープ内でその変数の型が保証され、それに応じて安全にその変数を操作できます。
各条件分岐内では型が絞れていて、コンパイルエラーは起きない
function padLeft(value: string | number) {
if (typeof value === "string") {
return `-${value}-`;
} else {
return value.toString();
}
}
class Bird {
fly() {
console.log("bird flies");
}
}
class Dog {
bark() {
console.log("dog barks");
}
}
function move(animal: Bird | Dog) {
if (animal instanceof Bird) {
animal.fly();
} else {
animal.bark();
}
}
as const
文字列、数値、BigInt、真偽値リテラルに対してつけられるリテラル型がプリミティブにwidenningしないリテラル型=readonlyになる
// 中は書き換え可能なため、リテラル型がstringに置き換わる
// よって、string[]型
const names1 = ["aaa","bbb","ccc"]
// constのため、書き換え不可(readonly)、さらにリテラル型のままになる
// readonly["aaa","bbb","ccc"] 型
const names1 = ["aaa","bbb","ccc"] as const
モジュールとカプセル化
モジュール内で定義された変数はそのモジュール内をスコープとしてもつ。変数を変更できるのは、そのモジュール内だけから
import export
import { } from "モジュール名"
// export defaultの場合
import 変数名from "モジュール名"
@types
TypeScriptでもジュルーが書かれていない場合に、@typesパッケージをインストールして型情報をインストールする