TypeScript非内蔵の汎用的な型
そもそも
主にtype-challengesに挑戦する過程で学んだ、TypeScript非内蔵の汎用的な型をまとめてみた。
主な依存パッケージ
"devDependencies": { "typescript": "5.6.3" }
"devDependencies": { "typescript": "5.6.3" }
オブジェクト
DeepReadOnly
type DeepReadOnly<T> = { readonly [K in keyof T]: T[K] extends isPrimitive<T[K]> ? T[K] : DeepReadOnly<T[K]> }
type DeepReadOnly<T> = { readonly [K in keyof T]: T[K] extends isPrimitive<T[K]> ? T[K] : DeepReadOnly<T[K]> }
DeepRequired
type DeepRequired<T> = { [K in keyof T]-?: T[K] extends isPrimitive<T[K]> ? T[K] : DeepRequired<T[K]> }
type DeepRequired<T> = { [K in keyof T]-?: T[K] extends isPrimitive<T[K]> ? T[K] : DeepRequired<T[K]> }
DeepPartial
type DeepPartial<T> = { [K in keyof T]?: T[K] extends isPrimitive<T[K]> ? T[K] : DeepPartial<T[K]> }
type DeepPartial<T> = { [K in keyof T]?: T[K] extends isPrimitive<T[K]> ? T[K] : DeepPartial<T[K]> }
PickRequired
type PickRequired<T> = Pick< T, { [K in keyof T]: T[K] extends Required<T>[K] ? K : never }[keyof T] >
type PickRequired<T> = Pick< T, { [K in keyof T]: T[K] extends Required<T>[K] ? K : never }[keyof T] >
PickOptional
type PickOptional<T> = Pick< T, { [K in keyof T]: T[K] extends Required<T>[K] ? never : K }[keyof T] >
type PickOptional<T> = Pick< T, { [K in keyof T]: T[K] extends Required<T>[K] ? never : K }[keyof T] >
OmitNever
type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] }
type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] }
ValueOf
type ValueOf<T> = T extends Record<string, infer U> ? U : never type ValueOf<T extends Record<string, unknown>> = T[keyof T]
type ValueOf<T> = T extends Record<string, infer U> ? U : never type ValueOf<T extends Record<string, unknown>> = T[keyof T]
Diff
type Diff<T, U> = Omit<T & U, keyof (T | U)>
type Diff<T, U> = Omit<T & U, keyof (T | U)>
配列
UnpackArray
type UnpackArray<T extends any[]> = T extends (infer U)[] ? U : never
type UnpackArray<T extends any[]> = T extends (infer U)[] ? U : never
NonEmptyArray
type NonEmptyArray<T extends any[]> = [T, ...T[]]
type NonEmptyArray<T extends any[]> = [T, ...T[]]
Length
type Length<T extends any[]> = T['length']
type Length<T extends any[]> = T['length']
First
type First<T extends any[]> = T extends [] ? never : T[0] type First<T extends any[]> = T extends [infer U, ...any[]] ? U : never
type First<T extends any[]> = T extends [] ? never : T[0] type First<T extends any[]> = T extends [infer U, ...any[]] ? U : never
Last
type Last<T extends any[]> = [any, ...T][T['length']] type Last<T extends any[]> = T extends [...infer _, infer L] ? L : never
type Last<T extends any[]> = [any, ...T][T['length']] type Last<T extends any[]> = T extends [...infer _, infer L] ? L : never
Push
type Push<T extends any[], U> = [...T, U]
type Push<T extends any[], U> = [...T, U]
Pop
type Pop<T extends any[]> = T extends [...infer I, infer _] ? I : never
type Pop<T extends any[]> = T extends [...infer I, infer _] ? I : never
Unshift
type Unshift<T extends any[], U> = [U, ...T]
type Unshift<T extends any[], U> = [U, ...T]
Flatten
type Flatten<T extends any[] | unknown> = T extends [] ? [] : T extends [infer First, ...infer Rest] ? [...Flatten<First>, ...Flatten<Rest>] : [T]
type Flatten<T extends any[] | unknown> = T extends [] ? [] : T extends [infer First, ...infer Rest] ? [...Flatten<First>, ...Flatten<Rest>] : [T]
タプル
TupleToUnion
type TupleToUnion<T extends readonly any[]> = T[number]
type TupleToUnion<T extends readonly any[]> = T[number]
TupleToObject
type TupleToObject<T extends readonly any[]> = { [K in T[number]]: K }
type TupleToObject<T extends readonly any[]> = { [K in T[number]]: K }
文字列
Capitalize
type Capitalize<T extends string> = T extends `${infer U}${infer V}` ? `${Uppercase<U>}${V}` : never
type Capitalize<T extends string> = T extends `${infer U}${infer V}` ? `${Uppercase<U>}${V}` : never
KebabCase
type KebabCase<T extends string> = T extends `${infer U}${infer V}` ? V extends Uncapitalize<V> ? `${Uncapitalize<U>}${KebabCase<V>}` : `${Uncapitalize<U>}-${KebabCase<V>}` : T
type KebabCase<T extends string> = T extends `${infer U}${infer V}` ? V extends Uncapitalize<V> ? `${Uncapitalize<U>}${KebabCase<V>}` : `${Uncapitalize<U>}-${KebabCase<V>}` : T
Trim
type Space = ' ' | '\t' | '\n' type Trim<T extends string> = T extends | `${Space}${infer U}` | `${infer U}${Space}` ? Trim<U> : T
type Space = ' ' | '\t' | '\n' type Trim<T extends string> = T extends | `${Space}${infer U}` | `${infer U}${Space}` ? Trim<U> : T
Replace
type Replace< T extends string, U extends string, V extends string, > = T extends `${infer A}${U}${infer B}` ? `${A}${V}${B}` : T
type Replace< T extends string, U extends string, V extends string, > = T extends `${infer A}${U}${infer B}` ? `${A}${V}${B}` : T
StringToUnion
type StringToUnion<T extends string> = T extends `${infer U}${infer V}` ? U | StringToUnion<V> : never
type StringToUnion<T extends string> = T extends `${infer U}${infer V}` ? U | StringToUnion<V> : never
Set
UnpackSet
type UnpackSet<T> = T extends Set<infer U> ? U : never
type UnpackSet<T> = T extends Set<infer U> ? U : never
Map
MapKeys
type MapKeys<T> = T extends Map<infer U, unknown> ? U : never
type MapKeys<T> = T extends Map<infer U, unknown> ? U : never
MapValues
type MapValues<T> = T extends Map<unknown, infer U> ? U : never
type MapValues<T> = T extends Map<unknown, infer U> ? U : never
関数
Args
type Args<T extends (...args: any) => any> = T extends (...args: infer U) => any ? U : never
type Args<T extends (...args: any) => any> = T extends (...args: infer U) => any ? U : never
ちょっと特殊な型
isPrimitive
type Primitive = string | number | bigint | boolean | symbol | null | undefined type isPrimitive<T> = T extends Primitive ? true : false
type Primitive = string | number | bigint | boolean | symbol | null | undefined type isPrimitive<T> = T extends Primitive ? true : false
UnionToIntersection
Union型をIntersection型に変換する型
type UnionToIntersection<U> = ( U extends any ? (arg: U) => void : never ) extends (arg: infer I) => void ? I : never
type UnionToIntersection<U> = ( U extends any ? (arg: U) => void : never ) extends (arg: infer I) => void ? I : never
参考:https://qiita.com/suin/items/93eb9c328ee404fdfabc
Equal
二つの型を厳密に等価判定する型
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false
参考:https://github.com/microsoft/TypeScript/issues/27024#issuecomment-421529650
Common
二つの型に共通するプロパティを抽出する型(自作)
type Common< T extends Record<string, unknown>, U extends Record<string, unknown>, > = Pick< T, { [K in keyof T & keyof U]: Equal<T[K], U[K]> extends true ? K : never }[keyof T & keyof U] >
type Common< T extends Record<string, unknown>, U extends Record<string, unknown>, > = Pick< T, { [K in keyof T & keyof U]: Equal<T[K], U[K]> extends true ? K : never }[keyof T & keyof U] >
参考書籍
プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで