Open17

Type-Challenges

dyecmadyecma

Promiseから再起的に待機型を取得する

type MyAwaited<T extends Promise<unknown>> = T extends Promise<infer K>
  ? K extends Promise<any>
    ? MyAwaited<K>
    : K
  : never;
dyecmadyecma

Condition(C)によって型をスイッチする

type If<C extends boolean, T, F> = C extends true ? T : F;
dyecmadyecma

配列の含有判定

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

ここはまだ理解できてないです。
関数の中身にあまり意味はない?

dyecmadyecma

配列型のループ処理をするときには、

T extends [infer K, ...infer Rest]

のような記述をして再起的に処理する。

dyecmadyecma

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"

のようになる

dyecmadyecma

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"
dyecmadyecma

特定キーの値の方のみ抽出

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
dyecmadyecma

配列の先頭に追加

type Unshift<T extends any[], U> = [U,...T]
dyecmadyecma

関数のパラメーターを抽出

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]
dyecmadyecma

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;
}
dyecmadyecma

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;
}
dyecmadyecma

指定したフィールドのみを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">>

&演算子が入ると見づらくなる。改善の余地あり。
→ あまり良い方法が見つからない。

dyecmadyecma

「必須モデル・オプショナルエラー」と「オプショナルモデルと必須エラー」の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() };
dyecmadyecma

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;
}
 */

`