d.sunnyone.org
sunnyone.org

ページ

2023-12-19

例でわかるTypeScript Utility Types

これはTypeScript Advent Calendar 202319日目の記事です。例とともにUtility Typesの紹介をします。

Utility Typesとは

Utility Typesは、型から型を生成するためのTypeScript組み込みの型です。ライブラリを書いているような方ならば普段から使っていると思いますが、Utility Typesが何かわからない方もPartial<Foo>やRecord<string, string>なんかは見たことがあるのではないでしょうか。

どんなものがあるか?というのはドキュメントを見てもらえれば良いので、 詳細はそちらに譲りますが、今日はどういうときに便利なのか?という視点でUtility Typesの主要なものを分類しつついくつか紹介します。

TypeScriptが提供する型: 作るもの

Record<Keys, Type>

Recordは、Keysをキーとするプロパティを持ち、そのプロパティの値の型はTypeであるという型を生成する型です。これは主にデータを格納するためのオブジェクトの型を定義するときに使います。

type FooRecord = Record<"prop1" | "prop2", string>;
// → { prop1: string, prop2: string }

上記のようなケースだとベタに定義したほうが良いのでありがたみはないですが、keyの集合がすでに定義されている場合は便利です。

interface BarData {
    prop1: string;
    prop2: number;
    prop3: Date;
}

type BarDataErrors = Record<keyof BarData, string>
//  → { prop1: string, prop2: string, prop3: string}
stringのように広い型を指定することもできます。 
type StringRecord = Record<string, string>;

もともとRecordは

type Record<K extends keyof any, T> = {
    [P in K]: T;
};

と単純なのでこれを書いてもいいのですが、意図を明示できる上に簡潔に記述することができます。

TypeScriptが提供する型: 主にプロパティを加工するためのもの

Pick<Type, Keys> / Omit<Type, Keys>

PickはTypeのプロパティをKeysに絞った型を作ります。OmitはTypeのプロパティからKeysを外した型を作ります。ホワイトリストとブラックリストですね。

これらの使い方は幅広いのですが、既存の関数のラッパーを作るときに便利です。

例えば、ButtonというReactコンポーネントがあり、propとしてsizeを取るとき、このsizeを“large”に固定したLargeButtonというコンポーネントを作りたいようなケースを考えます。このときにLargeButtonのpropsの型をButtonからsizeプロパティを外したものとして作ると、Buttonと同じだがsizeは渡せないということを明確にすることができます。

type LargeButtonProps = Omit<ButtonProps, "size">;

function LargeButton(props: LargeButtonProps) {
    return <Button {...props} size="large" />
}

Extract<Type, Union> / Exclude<UnionType, ExcludedMembers>

ExtractはTypeからUnionに代入可能なものを取り出した型を作り、ExcludeはUnionTypeからExcludedMembersに代入可能なものを外した型を作ります。

これは、discriminated unionとあわせて使います。例えば、type: “success”のときは正常、type: “error”のときはエラーであるResultオブジェクトを考えます。

interface ResultSuccess {
    type: "success";
    data: Record<string, string>;
}

interface ResultError {
    type: "error";
    errorMessage: string;
}

// 既存ライブラリが合わさっている型だけexportしていたり
export type Result = ResultSuccess | ResultError;

このような場合に、ExtractやExcludeを使うと限定した型を得ることができます。

type SuccessType = Extract<Result, {type: "success"}>
// ResultSuccess
type NonSuccessType = Exclude<Result, {type: "success"}>
// ResultError

Partial<Type> / Required<Type>

Partialはプロパティを任意にし、Requiredはプロパティを必須にします。

Partialは例えばプロパティを部分的に受け取ってオブジェクトを更新するような関数を作る場合に使えます。

function updateFoo(foo: Foo, fields: Partial<Foo>) {
    return {...foo, ...fields };
}

もともとすべてのプロパティがrequiredとは限らないので、Partialと比べるとRequiredを素で使うケースは少ないと思いますが、他との組み合わせ、例えばPickで明確にして使う、ということもできます。

interface Foo {
    name?: string;
    age?: number;
    description?: string;
}

type Bar = Required<Pick<Foo, "name" | "age">>;

NonNullable

NonNullableはTypeからnullとundefinedを外します。

type NullableType = string | null;
type NonNullType = NonNullable<NullableType>;
// string

まあこれはわかりやすいですよね。

TypeScriptが提供する型: 関数に関するもの

関数の型に対して何かをする型は、汎用的に関数を取ってそれをどうにかする関数には不可欠なものですが、具体的な関数がわかっている場合にも以下のような使い方をすることができます。

ReturnType

ReturnTypeは関数の型から戻り値の型を得ます。

ReturnTypeは、関数はわかっているが型にアクセスできない、または定義されていないようなとき、例えばfactory的な関数でその型の明示がないようなケースのときに、その値を中継するなどのことをするために型が欲しいときに便利です。

function createFoo() {
    return {
        a: "abc",
        b: 123
    };
}

type CreateFooReturn = ReturnType<typeof createFoo>;
// { a: string, b: number }

Parameters

Parametersは、関数の引数の型を得られます。ラッパー関数を作るような場合に、既存の関数の型をそのままに引数を足したいなどのときに便利です。

function foo(a: string|number) {
    return a;
}

type FooParameters = Parameters<typeof foo>;
function wrappedFoo(a: FooParameters[0], b: string) {
    other(b);
    return foo(a);
}

Awaited

asyncな関数だとreturn typeがPromiseになりますが、そのままでは扱いにくいです。 そんなときにAwaitedを使うと剥がすことができます。

interface Foo {
    a: string;
    b?: number;
}

export async function createFoo(): Promise<Foo> {
    return {
        a: "bar",
        b: 123
    };
}

///

type RequiredFoo = Required<Awaited<ReturnType<typeof createFoo>>>;
// {a: string, b: number}

自分で作る

Utility TypesもTypeScriptで定義された型でしかないので、このような型は自分でも作ることができます。

例えば、部分的に任意にするPartialPartiallyというものを作ると以下のようになります。

// すでに定義されているHoge
interface Hoge {
    name: string;
    description: string;
    startAt: Date;
    finishedAt: Date;
}

type PartialPartially<T, K extends keyof T> = Partial<Pick<T, K>> & Pick<T, keyof Omit<T, K>>;
// 終わってない(finishしていない)かもしれない型を作る
type HogeMayUnfinished = PartialPartial<Hoge, "finishedAt">;

このように、型の操作の仕方と、型の操作そのものを分離しておくと、あとから見たときの悩み度合いが減って良いです。

おわりに

使わないに越したこともないものもありますが、どうしようもないときは知ってるか知らないかで型の扱いの難易度が変わってきます。

Utility Typeを使って、型操作を楽にしていきましょう。

2023-03-27

PHPerKaigi 2023に参加してきました

YAPC::Kyoto 2023を見て、PHPの世界をまた見てみるのいいかもと思ってPHPerKaigi 2023に行ってきました。結果、今のPHPを知れたという意味でもよかったし、ああいう雰囲気の場所が戻ってきたんだというのをまた体感できたという意味でもよかったです。スタッフの皆様、参加者の皆様お疲れ様でした。

以下、セッションのメモです。

---

勉強になったセッション

勉強になったのはこの「PHPの配列の内部実装について学びたくなった。

PHP4のときにガラケー向けアプリケーションを書いていて、arrayの性能に悩まされたことがあり、あのarrayはなあと思っていたのだけど、今更ではあるもののPHP7で真の配列が導入されたことをここで知れたのでよかった。

印象に残ったセッション

印象に残ったのは「名付けできない画面を作ってはならない - 名前を付けるとは何か

設計の話をするセッションだと思っていたのだけど、もうちょっと広く見ていてなるほどなと思った。気持ちの面も結構大事な話だと思う。

2023-03-21

YAPC::Kyoto 2023に参加してきました

京都観光も兼ねて、YAPC::Kyoto 2023に参加してきました。面白かった。遠征でのカンファレンスの参加は人生初なので、そういう意味でも新鮮な体験だった。

印象に残ったセッション

みんな面白かったのだけど、印象に残ったのはmacopyさんのデプロイ今昔物語だった。


よくわからないホスティング環境を借りてPHP(確か4だったと思う)のプログラムを「ホームページ」に置いてみたりとか、読めないPerlを書いて自分で困ったりしていた時代を思い出して、原点に帰った気分になれてとてもよかった。

遠征での学び

日曜日にYAPCで、京都にせっかく行くのだから土曜日は観光しようと決めていて、土曜日に観光するなら朝から行けたほうがいいよねということで金曜日に行って泊まり、日曜日遅くなるかもしれないから月曜日に帰ろうということで、結果3泊4日になった。歩数計を見ると40km近く歩いていることになっているので、結構満喫したと思う。

旅程に合わせてホテルを変えたので、服は洗濯しちゃえば運ぶの楽じゃないか?と思ってチャレンジしてみたものの、旅から帰ってきたあとコインランドリーに行き来して洗濯するのは結構しんどいというのは学びだった。洗濯と乾燥が一緒ならいいのにな~

荷物まわりはuzullaさんがテクニックをまとめてくれているので、今度参考にして楽をしたい。

俺的!遠方カンファレンスの参加体験向上テク 2023最新版 - uzullaがブログ https://uzulla.hateblo.jp/entry/2023/03/21/132421


2022-12-31

自室作業環境再構築(2022)

もともとリモートワーク的に作業する環境はあったものの、机にガタが来たのをきっかけに見直しをして作り直したので、その環境のメモ。もう数ヶ月前なのでちょっと情報が古かったりするのだけど、部屋の環境を作るときの参考になれば。

家具

デスク: FlexiSpot E8 (+ 120cm 天板)

まずはデスクから。今までのデスクはキーボードの棚を引き出して使って、上にプリンタ棚があるいわゆるPCデスクという感じ。地震でキーボードの棚が引き出せなくなったことをきっかけにサイズを測ってみたら幅100cm/奥行き50cmしかなくてなるほどこれは狭いなと。

いっそデスクは新調しようということで調べてみると、高さが調整できたほうが良さそうだなというところには来て、電動昇降をありにするか迷っていたのだけど、使っている人にヒアリングしてみると、ずっとそこで仕事するなら切り替えになって良いという話を聞いたので、まあ机はケチるところではないし買ってみようかと思い電動昇降つきを選択。

機種としては、昇降デスクの定番らしいFlexiSpotのメインストリーム系であろうE8を選択。120cm天板/ウォールナット色をセットで追加。

実際に使ってみると、新しい体験で面白かった。スタンディングであまり作業してはないのだけど、細かい調整が気軽にできたり、配線なんかのときに持ち上げて作業できるのは嬉しい。どの程度活用されていくかは未知数だけど、らしい使い方を探していくことを含めて楽しそうだと思う。

ちょっとがっかりだったのがウォールナットの色がだいぶ赤っぽかったこと。現物を確認できなかったので、仕方がないけど、暗めの茶色にしたかったので、セットだと他の選択肢はなかった。結局電動ユニットやコントローラは木ねじで止めるので板はなんでもよく、こだわるならかなでものというところの天板を合わせるのがテンプレみたい。サンプルを注文できるみたいだし、選ぶ手間とコスト感が合えばそのほうがいいかも。ホームセンターで板を買ってきてDIYする人もいるらしい。

あとは幅について。100cmから120cmになってだいぶよくなったが、まだちょっと狭い。140cmを置けないことはなかったから140cmにしたほうが良かったかなと思いつつ、今の環境だと無理やり感出るからこれでもよかったかな、みたいな、サイズに関してはちょっとモヤっとする感じになってはいる。奥行きについては10cm伸びたのと、モニターアームをつけたこともあって結構いい感じにはなった。

PCワゴン: 30cm✕60cmで天板が昇降するやつ

PCは台車に乗せてデスクの下に置こうと思ったのだけど、120cmの幅だとだいぶ狭くなってしまう。仕方がないので横に置くことにしたのだけど、PCの上が棚になっていれば実質デスクを幅広く使えるのでは?ということで、天板を昇降できるPCワゴンをAmazonの森をかき分けて探してきた。結果座っている側の高さにあわせるといい感じにデスクを延長できている。

下に入れる場合でもPCワゴンいろいろあるみたいだから、フィットするのを探してみるのもいいかも? 

ケーブルトレー: クランプ式/ワイヤータイプの40cm✕2セットのもの

デスクが昇降するので、卓上↔床の配線はなるべく少なくしたい。となると、天板側になんとかしてケーブルを持ってくる必要がある。そのためにケーブルトレーというものがあって、形がいろいろあるのだけど、こだわりがないので、Amazonの森をかき分けて2個セットなのにセールで1.5kというものを見つけたので購入。

手軽そうだったのでクランプ式を選んだのだけど、どうせネジ止めしているので、ネジ式でも良いとは思う。ただ、モニターアームのクランプと干渉しない位置に設置する必要があるので、そのときに融通が効きやすいものを選択するのが良いとは思う。

椅子:REC-128AX

20年前くらいに買った手動昇降つきでメッシュ背面なオフィスチェア。いいやつがあれば置き換えるかもしれない。

長く使っているから座面の合皮がやぶれてはがれてしまったのだけど、合皮補修シートを使って直したらいい感じに戻った。

タイルカーペット: サンゲツ スタイルキットループ

昔から使ってたニトリの安いパズルマットと比べると圧倒的に良くて、ずれないし質感も良い。素直におすすめできる。

定価は40x40で800円/枚くらいだけど、楽天で500円/枚くらいのところがあった。サンプルも送ってもらえるので、試してみると良いかも。

デスクの寿命から派生した環境再構築のPCまわり編。デスクが変わったので、PCまわりもいろいろ不便だった部分を直した。

PC周り

モニタ: I-O DATA LCD-GCWQ341XDB

モニタは34インチ ウルトラワイドモニタ(UWQHD: 3440x1440)を選択。これは正解だった。

モニタは6年くらい前に買った27インチ4Kのモニタ(LG 27UD58P-B)と15インチ4Kのモバイルディスプレイを使っていたのだけど、27インチ 4K はdot by dotで使うと163ppiと、21インチ フルHDの105ppiと比べると圧倒的に細かくてスケーリングしないと使えない。

現実的に使おうとするとWQHD (2560x1440) 相当が109ppiなので、150%スケーリングしたいところなのだが、XOrg環境はまだ浮動少数スケーリングが得意じゃないので、200%でフォントサイズを調整して使っていた。

フルHD (1920x1080)x3の環境に慣れているので、なるべく広く取りたいのだけど、4Kも 200% にするとフルHDになってしまって、全く広くなくなっていた。

24インチ WQHD(2560x1440)を2つ並べるか?いや122ppiは細かすぎる、WUXGA (1920x1200)を2つしかないか?などと考えていたところ、34インチ UWQHD(3440x1440)の存在に気づき、選択した結果ちょうどいい感じになった。

湾曲でないほうがいいかなと思ったのだけど、使ってみてるとそんな困らない気もする。経験したので、今選ぶならまた違った選択肢になりそう。

サブモニタ: EVICIV 15.6インチ モバイルディスプレイ 4K

前に買ったやつを34インチモニタの上につけている。チャットアプリやターミナルを置いたりしているが、必要なときに見上げて使うのはちょっと辛いのでなんとかしたいところ。

モニターアーム(メイン用): エルゴトロンLX

悩むくらいならこれ選んでおけという感じで出てくるが、実際使ってみて納得。 強いし、調整もしやすい。あまり動かさないからこれでなくてもいいのではとも思うけど、わざわざ他を選ぶほどのことでもない印象。

モニターアーム(サブ用): Bracwiser PCモニターアーム + エルゴトロン クイックリリースブラケット

モバイルディスプレイ用のアーム。重いものではないから安いやつでいいものの、メインモニタの上に配置するから高さがあるものということで選んだ。ただ、モバイルディスプレイはヘッドレスなPCのメンテ用でもあるからモニタ側に取り外し可能にするブラケットをつけて外せるようにした。

スピーカー: EDIFIER R19U2

今まで使っていたスピーカーは置きにくくケーブルの取り回しが大変で、もうちょっと音がいいやつが欲しいなと思ったのでリーズナブルで便利そうなやつを買った。4000円くらいなので音はすごくいいわけではないけど、この値段なら納得できるし、見た目も悪くない。 Amazonを見たら一緒に比較検討してたJBL Pebblesが6264円なので、たいていはこっちでいいと思う。音質はこっちのほうが良い。ただ、スピーカー本体にボリュームノブがついてないので、その意味ではR19Uのほうが便利。

マイク: マランツ M4U

昔から使っているもの。マイク〜って形をしている。 3000円くらいのくせに、もろもろの付属なんかに比べるとずっと音質が良いのでおすすめ。

マイクスタンド: anctarc マイクスタンド

USB Type-C 充電器: Baseus PD 充電器 65W 6-IN-1 1250W 電源タップ

机の上にUSB PDの口とAC 100Vの口を出すために買った。サイズ感、配置の安定感なんかがよくてこれは正解。65Wで十分かどうかは注意だけど、足りないなら100Vの口から増やすのもあり。

USBハブ: ORICO USB3.0 ハブ 4ポート

これはハズレ。クランプで止めるのはいいけど、やっぱりバスパワーはつらそう。デバイスの認識がぱたぱたする。

工具

電動ドリル: makita DF033DZ

FlexiSpotは電動ドリルで木ねじを締めることを期待しているので、電動ドリルを買った。makitaの掃除機を持っていて、そこで10.8Vのバッテリーを使っているので、それに合わせてmakitaの10.8Vバッテリーを使う電動ドリルを買った。ご家庭にmakita色のものが増えるのは不思議な感じだけど、安心感がある。

makitaの工具的には14.4Vバッテリーが今の主力のようで、バッテリーの前提がなければそこから選ぶのが良いと思う。3kくらいの安いやつを選んでしまっても良い気もするけど。

今までの机を解体するときに便利かなと思って買った六角ドリルピットがほかでも使えてすごく便利だった。併せて買うと良いかも。

作業

デスクまわりほぼ一式作ったのでだいぶ疲れた。本当は既存環境も一応維持してBlue/Green的に構築しようと思ったのだけど、作業エリアのことを考えてなくて、結局復帰までPCまわりが使えなくなってしまった。

一気に注文したわけではなく、順番に頼んでいったのだけど、出来上がっていくのを見ながら軌道修正できたのでこれは正解だった。例えばPCワゴンなんか最初の設計にはなかった。建築にアジャイル要素は難しいよなと思っていたのだけど、活きる部分もあるなと思った。翌日到着するいまの通販インフラさまさまだけど。

参考

 設計にあたりリモートワークとスタンディングデスクや周辺事情 - tech.guitarrapc.cómには大いに参考になった。感謝。

 

2022-11-03

PulseAudioのデバイス接続時自動切替とデフォルト入出力の設定

Ubuntuを再起動すると入力デバイスが変わって音が拾えなくなる問題が起きていたのだけど、デバイス接続時にPulseAudioがデフォルトデバイスを自動切り替えする機能を切ればよくなりそうなので、無効にする方法をメモしておく(この環境では結果的にUSBオーディオデバイスがいくつもぶら下がっている形になっているので、認識順やつないでいる状態で順番が変わったりするのだと思う。バスパワーのハブにつながったカメラが怪しい)

PulseAudio 自動切り替えの無効化

自動切り替えを無効化するには、/etc/pulse/default.pa のmodule-switch-on-connectの記述をコメントアウトする。

### Use hot-plugged devices like Bluetooth or USB automatically (LP: #1702794)
##.ifexists module-switch-on-connect.so
##load-module module-switch-on-connect
##.endif 

デフォルト入出力デバイスの設定

これの前はデフォルトの設定を変える方法を試していたので、それも併せてメモしておく。

1. デバイスのnameを探す

汎用的には、pacmd list-sources / pacmd list-sinks でデバイスのname:を探す。
$ pacmd list-sources
(略)
  * index: 8
        name: <alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback>
(略)

ここで出てきたname: を後で使う。アスタリスクは今デフォルトに選ばれているもの。

GNOMEの設定の「サウンド」で選択していれば、pactl get-default-source / get-default-sinkでも得られる。

$ pactl get-default-source
alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback

2. 設定ファイルに記述する

/etc/pulse/default.pa.d/00-default-device.conf に以下のように記述する。
set-default-source alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback
set-default-sink alsa_output.usb-Resonessence_Labs_Resonessence_Labs_HERUS-00.iec958-stereo
なお、pactlコマンドでも変えることができるので、シェルスクリプトに書くのも可。
$ pactl set-default-source alsa_input.usb-C-Media_Electronics_Inc._MARANTZ_M4U_20190520-00.mono-fallback

参考

安定のArchWiki。

PulseAudio/サンプル - ArchWiki

2021-12-17

React Transition Group と class の状態

 React Transition Group CSSTransition はtransitionのためのclassをelementに付与してくれるのだけど、使う機会があるたびにどういう状態があるのか思い出してる気がするので、図にしておくことにした。



2021-09-22

Linux/Windowsデュアルブート環境のインストール方針

自分のWindows PCはLinux (Ubuntu) をインストールしてデュアルブートにすることが多い。 端末のタイプによってインストールの仕方が異なり、このやりかたが参考になるかもしれないので書いておく。
  1. 空のディスクが複数ある場合→1台ずつWindows, Linuxとインストール
  2. 空のディスクが1つのみの場合→Windowsをインストール後空いたところにLinuxをインストール
  3. Windowsが入ったディスクがある場合→パーティションを縮小してLinuxをインストール

空のディスクが複数ある場合

複数のディスクがPCに搭載できる/したい場合、それぞれにインストールする。まずWindowsをひとつのディスクにインストールしてから、別のディスクにLinuxをインストールする。 一番無難なかたち。

この場合では、データの受け渡しをするためのNTFSパーティションをLinux側のディスクにちょろっと用意しておくと、Windows側のCドライブをマウントしなくてよくて便利だったりする (NTFSマウントはなんだかんだあることがあるので)

空のディスクが1つのみの場合

ディスクが1つのみの場合、Windowsインストール時に容量を指定して(例えば半分)パーティションを作成し、インストール後、 空いた領域にLinuxをインストールする。 手元にあるPCがこのパターンで、こんな構成。

ディスク /dev/nvme0n1: 931.53 GiB, 1000204886016 バイト, 1953525168 セクタ
Disk model: WDS100T1X0E-00AFY0                      
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: gpt
ディスク識別子: B1EA5819-908C-4E4F-8542-CCBD4314BXXX

デバイス        開始位置   最後から    セクタ サイズ タイプ
/dev/nvme0n1p1      2048     206847    204800   100M EFI システム
/dev/nvme0n1p2    206848     239615     32768    16M Microsoft 予約領域
/dev/nvme0n1p3    239616  818331110 818091495 390.1G Microsoft 基本データ
/dev/nvme0n1p4 818331648  819437567   1105920   540M Windows リカバリ環境
/dev/nvme0n1p5 819437568 1600688127 781250560 372.5G Linux ファイルシステム

この順番の理由は、Windowsが作る予約領域をきちんと作成したいというのと、ブートローダ(grub)を持つのはLinuxになるため。

Windowsが入ったディスクがある場合

ノートPCなど、最初からWindowsが入ったディスクの場合、まず回復ディスクを作成し、 その後Windowsのディスクの管理からボリュームを縮小、空のディスクが1つのときと同様に空いた領域にLinuxをインストールする。