const Component = () => <div data-foo={xxx} />
みたいに色々な値を data-attribute に渡してみました。
結果:
undefined
,null
: そもそも追加されないNaN
: ワーニングが出る- そのほか: 文字列に変換されて値としてセットされる
検証に使ったデモがこちら
const Component = () => <div data-foo={xxx} />
みたいに色々な値を data-attribute に渡してみました。
結果:
undefined
, null
: そもそも追加されないNaN
: ワーニングが出る検証に使ったデモがこちら
JSON.stringify(undefined)
は undefined
です。 以上です。
JSON.stringify(undefined) の結果は?
— panda noir (@le_panda_noir) 2022年1月20日
正答率10%。みんな undefined を渡したときの挙動を知らなかったっぽいですね(僕も知りませんでした)
JSON.stringify に undefined を渡した時の挙動は ECMAScript (ECMA-262) の JSON.stringify でちゃんと定義されています。
他の例外パターンとして、循環があると TypeError が起きるようです。
a = []; a[0] = a; my_text = JSON.stringify(a); // throw a TypeError.
また、JSON.stringify(function() {})
も undefined
になります。
JSON.stringify の型をみると string
しか返ってこないことになっています。 2017 年に issue が建てられていますがまだ解決していません。https://github.com/microsoft/TypeScript/issues/18879
コールバックrefの実装方法によって ref が渡されるタイミングが異なります。
<element ref={(ref) => {}} />
): 再レンダリングされるたびに呼び出されるインライン関数で渡すと、再レンダリングのたびに2回呼び出されます。1回目の呼び出しでは null が渡され、2回目では element が渡されます。
どちらの実装方法でも、マウント時には element が渡され、アンマウント時には null が渡されます。
https://ja.reactjs.org/docs/refs-and-the-dom.html#caveats-with-callback-refs
In short:
そもそもジェネリクスの目的は 「引数の型だけちょびっと異なる型を上手く扱いたい」 というのが原点です
const id = <T>(x: T) => x; const toString = <T>(x: T) => `${x}`;
id 関数も toString 関数も、引数の型として number も string も受け付けています。
このように、ジェネリクスは引数の型を可変にするために使われるのが一般的 です。
引数にジェネリクスを使わないパターンは ごく一部の例外を除いて存在しません (その例外についてが本題なので後述します)。
例えば常に例外を投げて失敗する async 関数を考えます
const throwError = <T>(): Promise<T> => { throw new Error(); };
型検査も通りますし、使うこともできます。ただ、Promise<T>
の代わりに Promise<never>
と書いても同じです。
const throwError = (): Promise<never> => { throw new Error(); };
このように、返り値にジェネリクスを使わない形に書き換えられるケースがほとんどです。
見てきたように基本的に返り値にだけジェネリクスを使うケースはありませんが、レアな例外の一つが reference を返すケースです。React ユーザーであればすぐピンとくると思います。
たとえば const divRef = useRef<HTMLDivElement>(null)
のようなコードを書いたことがありませんか? この場合、引数は null ですが、返り値が RefObject<HTMLDivElement>
です。まさに引数にはジェネリクスを使わないのに返り値にジェネリクスを使うケースです。
このように、状態が変化しうるものが返り値のときは、返り値にだけジェネリクスが現れます。
fetch 関数も、返り値にのみジェネリクスを使う例外の一つです。
const fetchAPI = <T>(endpoint: string): Promise<T> => fetch(endpoint).then((res) => res.json());
ただし、この場合は実はジェネリクスを使わなくても書けます。
const fetchAPI = (endpoint: string) => fetch(endpoint).then((res) => res.json());
この場合、Promise<any>
が返ってきます。つまり、any を T に変更しているのです。実際には any なので当然、型安全性を損ないます。ただ、「API は滅多に変わらないから type guard なしでキャストしてしまおう」という判断は実際のプロダクトでも十分考えられます(type guard を手動で管理しようとすると結構な労力になるので…)
このように型安全性を多少犠牲にしつつ利便性をとる場合にも使われることがあります。当然ですが多用すべきではありません。
ジェネリクスが返り値に現れた場合、それらはほとんどの場合 unknown
あるいは never
で置き換えることができます。reference を返すケースを除けば、型安全性のためにも返り値だけにジェネリクスを使うのはお勧めしません。
結論: 以下を .git/hooks/post-checkout に追加
#!/bin/sh PREV_BRANCH=`git reflog show -q | head -n1 | awk '{print $6}'` CACHE_DIR=~/.cache/my_project_node_modules mkdir -p $CACHE_DIR prev_node_modules_cache="$CACHE_DIR/$(git show $PREV_BRANCH:package.json | shasum | awk '{print $1}')-$(git show $PREV_BRANCH:package-lock.json | shasum | awk '{print $1}')" cur_node_modules="$CACHE_DIR/$(shasum package.json | awk '{print $1}')-$(shasum package-lock.json |awk '{print $1}')" if [ "$prev_node_modules_cache" = "$cur_node_modules" ]; then exit fi mv node_modules $prev_node_modules_cache if [ -d "$cur_node_modules" ]; then mv $cur_node_modules node_modules else npm ci fi
package.json と package-lock.json をキーに、node_modules を ~/.cache/my_project_node_modules
へ保存している。
node_modules をコピーではなく移動させているだけなので、かなり軽快に動作する。
(ざっとしか検証してないので、動かなかったらコメントお願いします)