【TypeScript】
変数user
の型はname: string; gender: string; age: number
。
const user: {
name: string;
gender: string;
age: number;
} = {
name: "sho",
gender: "man",
age: 31,
};
type構文を使うと、すでにある型に命名できる。
type 型名 = 型;
type userType = {
name: string;
gender: string;
age: number;
};
const user: userType = {
name: "sho",
gender: "man",
age: 31,
};
userType
はname: string; gender: string; age: number
型に名前をつけたもの。
user
というオブジェクトの型をuserType
で指定できる。
type
はオブジェクト型以外にもどんな型にも名前をつけることが可能なのに対して、
interface
はオブジェクト型だけに扱うことができる。
interface userType {
name: string;
gender: string;
age: number;
}
const user: userType = {...}
オプショナルなプロパティ
オプションという名前だけあって(?)、あってもなくてもいいようなプロパティを示す。
宣言する場合はプロパティ名の後ろに?
をつけてあげる。
user2はage
プロパティを持っていないので、user2.age
でプロパティアクセスしたときの結果はundefined
となる。
type Obj = {
name: "sho";
gender: "man";
age?: 31;
};
const user1: Obj = {
name: "sho", gender: "man", age: 31,
};
const user2: Obj = {
name: "sho", gender: "man",
};
console.log(user1.age); // 31
console.log(user2.age); // undefined
age
がオプショナルなプロパティじゃない場合は、user2
がage
プロパティを持っていない時点で型エラーとなる。
TypeScriptさんがコンパイルエラーを教えてくださる🥺
部分型関係
部分型関係はTypeScriptの型システムの根幹をなす要素の一つであり、TypeScriptを理解するためにはとても重要な概念、らしい!
部分型とは
2つの型の互換性を表す概念。
型Sは型Tの部分型である、みたいな言い回しをする。S extends T
と表せる。
以下は型Sが型Tの部分型である例
条件: 型Sが型Tに含まれる全てのプロパティを持っていること
type T = {
a: number;
};
type S = {
a: number;
b: string;
};
使い方
type T = { a: number; };
type S = { a: number; b: string; };
const s: S = { a: 1, b: "hello" }; // S型のオブジェクト
function takesT(arg: T) {
console.log(arg.a);
console.;log(arg.b); // T型はaプロパティしかないため、これはできない
}
takesT(s); // S型のオブジェクトはT型として使える
関数takesTはT
型の引数を要求するけど、S
型のオブジェクトs
を引数(T型)に渡すことができる。
これは型Sが型Tの部分型であるため。
まとめ
型Sが型Tの部分型 (S extends T)の場合は、型Sのオブジェクトは型Tとしても使用できる
型引数
書き方:type 型名<型引数名>
以下のように型引数を持つHuman
型はジェネリック型と呼ばれる。
ジェネリック型を使う時は、const 変数: 型 <型>
と書く。
type Human<T> = {
name: string;
additionalInfo: T;
};
const humanWithAge: Human<number> = {
name: "Alice",
additionalInfo: 30 // additionalInfo は number 型
};
const humanWithOccupation: Human<string> = {
name: "Bob",
additionalInfo: "Engineer" // additionalInfo は string 型
};
型引数は1つだけでなく複数あってもOK
type Human<T, K> = {...}
部分型関係による型引数の制約
Person
型はHasName
型の部分型。
type HasName = {
name: string;
};
type Person = {
name: string;
age: number; // 追加のプロパティ
};
type Family<Parent extends HasName> = {
mother: Parent;
father: Parent;
};
type MyFamily = Family<Person>;
const family: MyFamily = {
mother: { name: "Alice", age: 35 },
father: { name: "Bob", age: 40 },
};
type Family<Parent extends HasName>
Family
型は型引数Parent
を持っている。
Parent extends HasName
Parent
に渡される型はHasName
の部分型でないといけない、という制約(constraint)を表す。
この場合、Parent
に渡せる型は最低でもname: string
を持っている必要がある!
部分型関係による型引数の制約とはつまり...
配列の型
// number型の要素を持つ配列型
const arr1: number[] = [1, 2, 3];
// string型の要素を持つ配列型 Array<> のようにも書ける
const arr2: Array<string> = ["a", "b", "c"];
// 型推論で (string | number | boolean)[] となる
const arr3 = [1, "a", true];
// 読み取り専用の配列型
const arr4: readonly number[] = [1, 2, 3];
arr4[2] = 100; // readonlyなので書き換えは不可
タプル型
配列の要素数が固定の代わりに、それぞれの要素に異なる型を設定できる
const tuple: [number, string] = [1000, "aiueo"];
(number[]
のような配列型は、要素数は何個でもいいが全ての要素の型は決まっている)
TypeScriptの関数
関数宣言で関数を作成
function 関数名( 引数: 型 ): 返り値の型 { 処理 }
返り値のある関数
function func1(arg: number): number[] {
const result = [];
result.push(arg);
return result;
}
返り値のない関数
function displayMessage(message: string): void {
console.log(message);
}
関数式で関数を作成
const 変数 = function ( 引数: 型 ): 返り値の型 { 処理 }
functionの後ろに関数実行がない。この関数を使用するには変数に格納すること。
コード
type WorkDetails = {
hoursWorked: number;
hourlyRate: number;
};
const totalPayment = function (workDetails: WorkDetails): number {
return workDetails.hoursWorked * workDetails.hourlyRate;
};
const myWorkDetails: WorkDetails = {
hoursWorked: 160,
hourlyRate: 2000,
};
console.log(totalPayment(myWorkDetails));
分割代入をつかった書き方(変更箇所だけ)
const totalPayment = function ({hoursWorked, hourlyRate}: WorkDetails): number {
return hoursWorked * hourlyRate;
};
アロー関数式で関数を作成
コード
const 関数名 = ( 引数: 型 ): 返り値の型 => { 処理 }
const displayMessage = (message: string): void => {
console.log;
};
可変長引数の宣言
コード
const displayNumber = (...args: number[]): void => {
for (const number of args) {
console.log(number);
}
};
displayNumber(1, 2, 3, 4, 5);
オプショナルな引数
コード
const calculatePrice = (price: number, includeTax?: boolean): number => {
const taxRate = 0.1; // 10% の税率
if (includeTax) {
return price * (1 + taxRate);
} else {
return price;
}
};
const priceWithoutTax = calculatePrice(1000); // 税抜価格
const priceWithTax = calculatePrice(1000, true); // 税込価格
コールバック関数
関数の引数に渡される関数のこと
コールバック関数
関数の型
( 引数: 型 ) => 返り値の型
で表す。
関数の型
const displayName: (name: string) => void = (name: string): void =>
console.log(name);
// 関数の型に type で別名をつけられる
type F = (name: string) => void;
const displayName: F = (name: string): void => console.log(name);
関数型の部分型関係
コード
const fromAge = (age: number): HasNameAndAge => ({
name: "John Smith",
age: age,
});
// (age:number) => HasName型の関数
const f: (age: number) => HasName = fromAge;
const obj: HasName = f(100); // fromAge(100)と同等
fromAge 関数は、name と age の両方のプロパティを持つオブジェクトを返します。
しかし、f は (age: number) => HasName 型の関数として定義されています。つまり、f は HasName 型のオブジェクトを返す関数とみなされます。
したがって、f(100) の呼び出し結果は、HasName 型のオブジェクトとして扱われます。これは name プロパティのみが利用可能で、age プロパティは HasName 型には存在しないため、直接アクセスすることはできません。
部分型が成り立つので、HasNameAndAge型のオブジェクトを、HasName型のobjに代入できる
あとで整理する!!!!
ジェネリック関数
型引数を持つ関数。
ジェネリック関数は、実行時に特定の型を受け取り動作する。
様々な型に対して、同じ関数を再利用することができる!
ジェネリック関数の宣言
関数名 <型引数>
ジェネリック関数の呼び出し
関数名 <型引数>(引数)
// 引数を返すだけの関数
const func = <T>(arg: T): T => arg;
console.log(func<string>("message")); // message
console.log(func<number>(123456)); // 123456
関数の呼び出し側の型引数は省略可能。
これは引数の型から、型引数Tの値を推論してくれるため。(TS優秀☺️)