Open17
Type-Challenges
への挑戦
Promiseから再起的に待機型を取得する
type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer K>
? K extends Promise<any>
? MyAwaited<K>
: K
: never;
Condition(C)によって型をスイッチする
type If<C extends boolean, T, F> = C extends true ? T : F;
配列型を結合する
type Concat<T extends unknown[], U extends unknown[]> = [...T,...U];
配列の含有判定
type Includes<T extends readonly any[], U> = T extends [infer K, ...infer Rest] ?
Equal<K, U> extends true?
true
: Includes<Rest, U>
: false
一致判定は以下のように書く。
type Equal<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? true : false
ここはまだ理解できてないです。
関数の中身にあまり意味はない?
配列への追加
type Push<T extends any[], U> = [...T, U]
配列型のループ処理をするときには、
T extends [infer K, ...infer Rest]
のような記述をして再起的に処理する。
optional以外のキーを抽出する
type RequiredKeys<T> = {
[K in keyof T]-?: {} extends { [P in K]: T[K] } ? never : K;
}[keyof T];
type Result = RequiredKeys<{
bool1?:boolean,
bool2:boolean,
num1?:number,
num2:number
}>
結果は
type Result = "bool2" | "num2"
のようになる
optionalのみ抽出
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends { [P in K]: T[K] } ? K : never;
}[keyof T];
type Result = OptionalKeys<{
bool1?:boolean,
bool2:boolean,
num1?:number,
num2:number
}>
結果は
type Result = "bool1" | "num1"
特定キーの値の方のみ抽出
type ExtractA<T> = T extends { a: infer R } ? R : never;
type Result1 = ExtractA<{b:1}>
type Result2 = ExtractA<{a:2}>
結果
type Result1 = never
type Result2 = 2
配列の先頭に追加
type Unshift<T extends any[], U> = [U,...T]
関数のパラメーターを抽出
type MyParameters<T extends (...args: any[]) => any> = T extends (...args: infer X) => any ? X : never;
例
const func1 = (arg1: string, arg2: number): void => {}
type Result = MyParameters<typeof func1>
結果
type Result = [arg1: string, arg2: number]
Optionalやundefinedをnullに変換 version1
type Undefined2Nullable<T> = {
[P in keyof T]-?: T[P] extends Required<T>[P] ? Required<T>[P] : Required<T>[P] | null
}
例
type Result = Undefined2Nullable<{ a: number; b?: string }>
結果
type Result = {
a: number;
b: string | null;
}
Optionalやundefinedをnullに変換 version2
再起的に変換
type NullableWihtoutOpt<T> = {
[P in keyof T]-?: T[P] extends Required<T>[P]
? T[P] extends object | undefined
? NullableWihtoutOpt<T[P]>
: Required<T>[P]
: NullableWihtoutOpt<T[P]> | null;
};
例
type Result = NullableWihtoutOpt<{
a: number;
b?: string;
obj1: { a: number; b?: string };
obj2?: { a: number; b?: string };
}>;
結果
type Result = {
a: number;
b: string | null;
obj1: NullableWihtoutOpt<{
a: number;
b?: string | undefined;
}>;
obj2: NullableWihtoutOpt<{
a: number;
b?: string | undefined;
}> | null;
}
指定したフィールドのみをoptionalにする
type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
例
type Result = PartialBy<{ id: number; name: string }, "id">;
結果
type Result = Omit<{
id: number;
name: string;
}, "id"> & Partial<Pick<{
id: number;
name: string;
}, "id">>
&演算子が入ると見づらくなる。改善の余地あり。
→ あまり良い方法が見つからない。
「必須モデル・オプショナルエラー」と「オプショナルモデルと必須エラー」のUnionを作る
type ResultWithError<T, E> =
| ({ [K in keyof T]: T[K] } & { error: undefined })
| ({ [K in keyof T]: undefined } & { error: E });
例
type ApiOutput = { model: { a: number; b: string } };
type Result = ResultWithError<ApiOutput, Error>;
結果
type Result = ({
model: {
a: number;
b: string;
};
} & {
error: undefined;
}) | ({
model: undefined;
} & {
error: Error;
})
交差型がみづらい。
使い方
const a: Result = { model: { a: 1, b: 'txt' }, error: undefined };
const b: Result = { model: undefined, error: new Error() };
Union型をOmitする
type T1 =
| {
a: string;
b: boolean;
c: number;
}
| {
a: string;
b: boolean;
d: string;
};
type ResultOfSimpleOmit = Omit<T1,'b'>
/**
type ResultOfSimpleOmit = {
a: string;
}
*/
type UnionOmit<T, U extends string> = T extends T ? { [K in Exclude<keyof T, U>]: T[K] } : never
type ResultOfUnionOmit = UnionOmit<T1, 'a'>
/**
type ResultOfUnionOmit = {
b: boolean;
c: number;
} | {
b: boolean;
d: string;
}
*/
`