ジェネリクス
型を引数として受け取る基本
関数実行時に引数で型を渡すことができる それを関数内で使えるfunction copy<T>(value: T): T {
let hone: T;
return value;
}
copy<string>("hello");
具体例
// number型
function test(arg: number): number {
return arg;
}
// string型
function test2(arg: string): string {
return arg;
}
test(1); //=> 1
test2("文字列"); //=> 文字列
//上の2つは共通化できそう
function test<T>(arg: T): T {
return arg;
}
test<number>(1); //=> 1
test<string>("文字列"); //=> 文字列
//※ Genericsでも型推論ができるので、引数から型が明示的にわかる場合は省略が可能
test("文字列2"); //=> "文字列2"
型推論
引数に型を書かなくても型推論してくれる この時の型は{ name : string}function copy<T>(value: T): T {
return value;
}
copy({name:"hello"}.name);
複数の型引数を定義する
function test<T, U, P>(arg1:T, arg2: U, arg3: P): P {
return arg3;
}
//※ Genericsでも型推論ができるので、引数から型が明示的にわかる場合は省略が可能
test("文字列", true, 4); //=> 4
プロパティを増やす関数
T & { id: string }はインターセクション型であり、オブジェクトに新しいプロパティを増やしたい場合の典型的な方法 Tと{ id: string }両方の型を持つという意味function plus<T>(obj: T): T & { id: string } {
const id = "ID";
return {
...obj,
id,
};
}
const obj1: {
id: string;
foo: number;
} = plus({ foo: 123 });
const obj2: {
id: string;
num: number;
hoge: boolean;
} = plus({
num: 0,
hoge: true,
});
console.log(obj1);
console.log(obj2);
//コンソール
{ foo: 123, id: 'ID' }
{ num: 0, hoge: true, id: 'ID' }
extends
パラメーターに制約を付ける こう書くと必ずオブジェクトでnameを含んでいることになるfunction copy<T extends { name: string }>(value: T): T {
return value;
}
copy({ name: "hello" });
keyof / T[K]
オブジェクトのプロパティ名をliteral Typesとして一覧で取得できるものinterface User {
name: string;
age: number;
}
type UserKey = keyof User;
UserKey は 'name' | 'age' というtype
K は、keyof T を満たすtypeであれば良いので、例えば
type UserName = User['name'];
とすると、UserName のtypeは string になる。 User['age'] とすれば number。
具体例1
interface Observable<T> {
pluck<K extends keyof T>(key: K): Observable<T[K]>;
}
let user: Observable<User>;
let name = user.pluck('name');
// Observable<string>
具体例2
type PropNullable<T> = {[P in keyof T]: T[P] | null};
interface Foo {
foo: string;
bar: number;
}
const obj: PropNullable<Foo> = {
foo: 'foobar',
bar: null,
};
//PropNullable<Foo>の型
{foo: string | null; bar: number | null; }
keyof / typeof
const obj = {
foo: "aaa",
111:222
}
type test = keyof typeof obj
//type test = "foo" | 111
typeofでobjの型を取得
keyofでliteral typesの一覧を取得
柔軟なジェネリクス
U extends keyof T でUはnameとageのユニオン型 こうすることで、第二引数にnameまたはage以外とれなくなるfunction copy<T, U extends keyof T>(val: T, key: U) {
return val[key]
}
const suda = copy({ name: 'masaki', age: 28 }, 'name')
console.log(suda)
// コンソール
masaki
classにgenericsを適用
class DataBase<T extends string | number | boolean> {
private data: T[] = [];
add(item: T) {
this.data.push(item);
}
remove(item: T) {
this.data.splice(this.data.indexOf(item), 1);
}
get() {
return this.data;
}
}
const stringDataBase = new DataBase<string>();
stringDataBase.add("apple");
stringDataBase.add('banana')
stringDataBase.add('grape')
stringDataBase.remove('banana')
console.log(stringDataBase.get())
//コンソール
[ 'apple', 'grape' ]
型のfor文であるMapped Types
interface person {
masaki: string,
age:28
}
type MappedTypes = {
[P in keyof person]:P
}
//MappedTypesの型
type MappedTypes = {
masaki: "masaki";
age: "age";
}
type MappedTypes = {
[P in keyof person]:string
}
//MappedTypesの型
type MappedTypes = {
masaki: string;
age: string;
}
具体例
type MyPartial<T> = { [K in keyof T]?: T[K] };
type T1 = MyPartial<{
foo: number;
bar: string;
}>;
//T1の型
{ foo?: number; bar?: string; }
型のif文であるConditional Types
イメージ三項演算子 トマト型がstringに代入できればnumberできなければbooleantype ConditionalTypes_1 = "tomato" extends string ? number : boolean;
//現在の型
type ConditionalTypes_1 = number
type ConditionalTypes_2 = string extends "tomato" ? number : boolean;
//現在の型
type ConditionalTypes_2 = boolean
具体例
Aがundefinedかどうかで動作を変えたい undefined型がA型の部分型であれば第一引数はあってもなくてもよいtype Func<A, R> = undefined extends A ? (arg?: A) => R : (arg: A) => R;
// 使用例
//undefined型がA型の部分型ではないのでfalse
const f1: Func<number, number> = (num) => num + 10;
const v1: number = f1(10);
//Aがundefinedなのでtrue
const f2: Func<undefined, number> = () => 0;
const v2: number = f2();
const v3: number = f2(undefined);
//Aはnumber | undefinedなのでtrue
const f3: Func<number | undefined, number> = num => (num || 0) + 10;
const v4: number = f3(123);
const v5: number = f3();
// エラー例
//この場合は引数が必要なのでエラー
const v6: number = f1();
Infer
//tomatoがtomatoプロパティをもったらtomato型
//なければ、boolean
type ConditionalTypesInfer = { tomato: "tomato" } extends { tomato: infer R }? R: boolean;
//tomatoがtomatoプロパティをもったらstring型
//なければ、boolean
type ConditionalTypesInfer = { tomato: "tomato" } extends { tomato: infer R }? string: boolean;
具体例
//まずはTをオブジェクトに限定
//引数にfooがあったらfooのプロパティ型、なければunknown
//最後にas anyを付けることでfooがないときのエラー回避
function getFoo<T extends object>(
obj: T
): T extends { foo: infer E } ? E : unknown {
return (obj as any).foo;
}
// 使用例
// numはnumber型
const num = getFoo({
foo: 123
});
// strはstring型
const str = getFoo({
foo: "hoge",
bar: 0
});
// unkはunknown型
const unk = getFoo({
hoge: true
});
// エラー例
getFoo(123);
getFoo(null);
distribution
1:引数'tomato'は'tomato'に代入できるのでnumber
2:引数'pump'はできないのでboolean
type Conditional<T> = T extends 'tomato' ? number : boolean
let temp : Conditional<'tomato'|'pump'>
//tempの型
let temp: number | boolean