Open8

【TypeScript】

shogoooshogooo

変数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,
};

userTypename: string; gender: string; age: number型に名前をつけたもの。
userというオブジェクトの型をuserTypeで指定できる。

typeはオブジェクト型以外にもどんな型にも名前をつけることが可能なのに対して、
interfaceはオブジェクト型だけに扱うことができる。

interface userType {
  name: string;
  gender: string;
  age: number;
}

const user: userType = {...}
shogoooshogooo

オプショナルなプロパティ

オプションという名前だけあって(?)、あってもなくてもいいようなプロパティを示す。
宣言する場合はプロパティ名の後ろに?をつけてあげる。

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がオプショナルなプロパティじゃない場合は、user2ageプロパティを持っていない時点で型エラーとなる。

TypeScriptさんがコンパイルエラーを教えてくださる🥺

shogoooshogooo

部分型関係

部分型関係は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としても使用できる

shogoooshogooo

型引数

書き方: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を持っている必要がある!

部分型関係による型引数の制約とはつまり...

shogoooshogooo

配列の型

// 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[]のような配列型は、要素数は何個でもいいが全ての要素の型は決まっている)

shogoooshogooo

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); // 税込価格
shogoooshogooo

コールバック関数

関数の引数に渡される関数のこと

コールバック関数

関数の型

( 引数: 型 ) => 返り値の型で表す。

関数の型
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に代入できる

あとで整理する!!!!

shogoooshogooo

ジェネリック関数

型引数を持つ関数。
ジェネリック関数は、実行時に特定の型を受け取り動作する。
様々な型に対して、同じ関数を再利用することができる!
ジェネリック関数の宣言
関数名 <型引数>
ジェネリック関数の呼び出し
関数名 <型引数>(引数)

// 引数を返すだけの関数
const func = <T>(arg: T): T => arg;

console.log(func<string>("message")); // message
console.log(func<number>(123456)); // 123456

関数の呼び出し側の型引数は省略可能。
これは引数の型から、型引数Tの値を推論してくれるため。(TS優秀☺️)