TypeScriptのTemplateStringTypesでorder関数
Array.prototype.sortにわたすorder関数の型定義
Playgroundはこちらいろいろ触ってみてください。
文章
sortにわたすorder関数を配列の要素がネストしているオブジェクトだったとき即時関数で書かなきゃいけないのが辛いというのが根底にある気持ちです。
僕が求めるorder関数は、型とsortしたいkey(string)をわたしてdesc, ascだけ決めればokみたいなインターフェースでした。
TypeScriptだと配列の要素がネストしているオブジェクトのときkeyがstringでかけないな〜というのが悩みでしたが、TemplateStringTypesでそれが解決できたので作って見ました。order
関数がそれに当たります。
options
の中にあるadapter
というのはsortの方法をカスタマイズするためのオプション引数です。
type NestColumn<T, U extends string> = T extends { [key: string]: unknown } ? `${U}.${Columns<T>}` : U; type Columns<T extends { [key: string]: unknown }> = { [K in keyof T]: K extends string ? NestColumn<T[K], K> : never }[keyof T] type Split<T extends string, D extends string> = T extends `${infer Head}${D}${infer Tail}` ? [Head, ...Split<Tail, D>] : [T]; type O<T, C extends unknown[]> = T extends { [key: string]: unknown } ? Target<T, C> : never type Target<T extends { [key: string]: unknown }, C extends unknown[]> = { [K in keyof T]: C extends [infer Head, ...(infer Tail)] ? K extends Head ? Tail extends [unknown, ...(infer _Tail1)] ? O<T[K], Tail> // Tailが残っているとき : T[K] // 解析終了 : never // 対象のkeyじゃないとき : never // そもそもCが空のとき }[keyof T] type Options<T extends { [key: string]: unknown }, C extends unknown[], S extends string> = { adapter?: (t1: Target<T, C>, t2: Target<T, C>, sort: S) => number } function order<T extends { [key: string]: unknown }, C extends Columns<T>>( columns: C, sort: "asc" | "desc", options?: Options<T, Split<C, ".">, "asc" | "desc">) { const keys = columns.split(".") as Split<C, "."> return (obj1: T, obj2: T) => { let t1: any = obj1; let t2: any = obj2; keys.forEach(key => { t1 = t1[key] t2 = t2[key] }) if (options?.adapter) return options.adapter(t1, t2, sort) if (sort === "asc") return t1 > t2 ? 1 : -1 return t1 < t2 ? 1 : -1 } }
最後に
こういうのでもライブラリ化していいのかな〜オレオレ型定義ライブラリ作りてぇ〜〜〜〜〜
anyを使ってしまいました。懺悔します。
let t1: any = obj1; let t2: any = obj2;
以上です。