Open3

型のメモ

marromugimarromugi

Union なオブジェクトからキーを抜きたい

普通にやったらうまく抜けなかった
以下のように調整したら取れた

type KeysOfUnion<T> = T extends T ? keyof T : never;

// 例
type A = {
    description: any,
    content: any
  } | {
    description: any,
    $ref: any
  },

type ResultA = KeysOfUnion<A> // "description" | "content" | "$ref"

Union なオブジェクトで keyof すると、never になってしまうため、KeysOfUnionをかませる
KeysOfUnionはジェネリクスの全てのUnion型に対して keyof T を適用することで、すべてのキーを取り出す型となります

オブジェクトがネストしている場合は以下のようにすることで型を抽出できます

type KeysOfUnion<T> = T extends T ? keyof T : never;
type KeysOfUnionObject<T> =  T extends Record<string, infer U> ?  KeysOfUnion<U> : never;

// 例
type A = {
  200: {
    description: any,
    content: any
  },
  400: {
    description: any,
    content: any
  },
  401: {
    $ref: any
  },
  403: {
    description: any,
  },
  500: {
    $ref: any
  },
}

type ResultA = KeysOfUnionObject<A> // "description" | "content" | "$ref"
marromugimarromugi

String か Union か判定するユーティリティ型

// `T` がユニオン型なら true、単一の `string` 型なら false を返す型
type IsUnion<T> = T extends string
  ? (string extends T ? false : true)
  : false;

// `T` が単一の `string` 型なら true、ユニオン型なら false を返す型
type IsString<T> = T extends string
  ? (string extends T ? true : false)
  : false;

union \subset string なので、

  1. A は string に含まれるか
  2. string に A は含まれるか
    という2回の判定を用いて union と string の判別を行う
marromugimarromugi

never な parameter をオプショナルにする

型パズルする時に便利な型。以下のような流れで変換する。

  1. never な型の key を抽出
  2. 抽出した key を omit
  3. omit した key をオプショナルで再追加
/**valueがneverのキーを抽出する */
type NeverKey<T> = {[K in keyof T]: T[K] extends never ? K : never}[keyof T]

/**neverを持つパラメータをオプショナルにする */
type OptionalizeNeverParameters<T> = Omit<T, NeverKey<T>> & {
	[key in NeverKey<T>]?: never
}

type Sample = { a: string, b: never };

const before: Sample = {
  a: "1234",
  // error: 'b' is declared here.
}

const after: OptionalizeNeverParameters<Sample> = {
  a: "1234",
}