Generics
型を引数として受け取ることができる仕組みです。
↓関数copyを実行するときに、型を引数として渡してあげることができます。
↓関数名と値の間に<T>と入れることで、Tが、今後どの場面でも使える型になります。
↓例えば値にも、戻り値にもTが使えますし、let user: T;のようにも使えます。
function copy<T>(value: T): T {
let user: T;
return value;
}
console.log(copy<string>('hello'));
じゃあこのTに入る型はどこで渡すかというと、console.log(copy('hello'));のように、
copyと引数の間に<string>として渡してあげます。
そうすると、<T>はstring型になります。
↓このTはパラメーターのように何個も渡せます。
↓T以降はU, Vのように書かれることが多いですが、名前はTやUでなく、何でも構いません。
function copy<T, U, V>(value: T): T {
let user: V;
return value;
}
console.log(copy<string, number, boolean>('hello'));
↓引数がオブジェクトの場合、
↓型アサーションではこのように{ name: string }と付ける必要がありましたが、
function copy<T>(value: T): T {
let user: T;
return value;
}
console.log(copy<{ name: string }>({ name: 'Quill'}));
↓genericsでは型推論してくれます。
function copy<T>(value: T): T {
let user: T;
return value;
}
console.log(copy({ name: 'Quill'}));
↓関数でT, Uと渡していてUについて何も書いていないときは、
↓Uはデフォルトでunknown型になっています。
function copy<T, U>(value: T): T {
let user: T;
return value;
}
console.log(copy({ name: 'Quill'}));
型パラメーターに制約を付ける
↓extendsを使います。
↓「オブジェクトかつnameを含んでいて、string型だ」という制約はこう書きます。
function copy<T extends { name: string }>(value: T): T {
let user: T;
return value;
}
console.log(copy({ name: 'Quill'}));
オブジェクトのキーのunion型を作る
↓Kは、type K = "number" | "name"というunion型になっています。
↓このように、オブジェクトのキーを取ってunion型にするものをkeyof演算子と言います。
type K = keyof { name: {}; number: number}
↓例えば、Uの型をTに依存させたいといったときに、
↓U extends keyof Tのようにすると、Uの型は、name | ageのunion型になります。
↓なので、Uの型はnameかageに限定されますし、関数の中でvalue[key];のように使えます。
function copy<T extends { name: string }, U extends keyof T>(value: T, key: U): T {
value[key];
return value;
}
console.log(copy({ name: 'Quill', age: 20}, 'age'));
classに対してgenericsを使う方法
↓定義するclass名の後にgenericsを追加します。
↓生成するときはインスタンス化するclass名(今回だったらnew)の後に追加します。
class LightDataBase<T extends string | number | boolean> {
}
const stringLightDataBase = new LightDataBase<string>();
Interfaceに対してgenericsを使う方法
↓interfaceを定義した名前の後に、<T>のように付けることができます。
interface TmpDataBase<T> {
id: number;
data: T[];
}
↓じゃあTの型はどこで定義するかというと、ここです。
const tmpDataBase: TmpDataBase<number> = {
id: 3,
data: [20]
}
Utility Types
utility = 実用性
型のlibraryです。
Typescriptに内蔵されたジェネリック型になります。
↓例えばPartial型というものがあります。これは、
interface ToDo {
title: string;
text: string;
}
type ToDoable = Partial<ToDo>
↓全部optional型になるUtility classです。
type ToDoable = {
title?: string;
text?: string;
}
↓Readonly classだったら、全てにreadonlyが付きます。
type ReadTodo = Readonly<ToDo>
type ReadTodo = {
readonly title: string;
readonly text: string;
}
Genericsの型のParameterにデフォルトの型を使う
↓Genericsの型のParameterは、TとかUのことです。
↓T = anyのようにすると使えるようになります。
interface ResponseData<T = any> {
data: T;
status: number;
}
let tmp: ResponseData;
Mapped Types
型のfor文です。反復させるような処理を行えます。
↓for文と同じくinを使います。
type MappedTypes = {
[P in 'tomato' | 'pumpkin']: P;
}
↓MappedTypesはiterationしています。
type MappedTypes = {
tomato: "tomato";
pumpkin: "pumpkin";
}
↓keyofと併用することもあります。
↓MappedTypesは先ほどと同じ内容です。
interface Vegetables {
tomato: string;
pumpkin: string;
}
type MappedTypes = {
[P in keyof Vegetables ]: P;
}
↓genericsを用いて、MappedTypesを色々なところで使えるようにする事もよくあります。
type MappedTypes <T> = {
[P in keyof T ]: P;
}
↓また、MappedTypesは先頭にreadonly, 末尾に?を付けることもできます。
type MappedTypes <T> = {
readonly [P in keyof Vegetables ]?: P;
}
----------------------------------------------------
//結果
type MappedTypes<T> = {
readonly tomato?: "tomato";
readonly pumpkin?: "pumpkin";
}
↓readonlyの頭に ー を付けると、Vegetablesのreadonlyが全て打ち消されます。
↓?の頭に ー を付けると、Vegetablesのoptional型が全て打ち消されます。
type MappedTypes <T> = {
-readonly [P in keyof Vegetables ]-?: P;
}
Conditional Types
型のif文みたいなものです。
↓tomato型が、string型に代入できるのであれば、number、できないのであればboolean
↓といった意味になります。
type ConditionalTypes = 'tomato' extends string ? number : boolean;
↓extendsを基準とし、左が右に代入できるような方向性のとき、Rが使えるという意味です。
↓infer Rはanyのようなもので、何でもOKです。
↓の上で、{ tomato: 'tomato' }から{ tomato: infer R }の型を推論します。
↓infer Rが、{ tomato: 'tomato' }に最も近くなるような型になります。
type ConditionalTypesInfer = { tomato: 'tomato' } extends { tomato: infer R } ? R : boolean;
よって今回は、ConditionalTypesInferが"tomato"型になります。
DistributiveConditionalTypes
↓DistributiveConditionalTypesというものがあって、
↓extendsの左側がunion型になっているとき、どうなるの?という意味を表現します。
↓結論、分配されます。
type DistributiveConditionalTypes<T> = T extends 'tomato' ? number : boolean;
let tmp: DistributiveConditionalTypes<'tomato' | 'pumpkin'>
つまり、number | booleanのunion型になります。
また、DistributiveConditionalTypesを使うときは、genericsを用います。