Skip to content

Instantly share code, notes, and snippets.

@0RIM0
Created January 30, 2025 15:14
Show Gist options
  • Save 0RIM0/bd71ce6f31fee789da6064960e6c46ef to your computer and use it in GitHub Desktop.
Save 0RIM0/bd71ce6f31fee789da6064960e6c46ef to your computer and use it in GitHub Desktop.
TypeScript で関数内で関数の返り値型を参照できた

TypeScript で明示的に型を指定する必要があって、それが関数の返り値になる場合
値に型を指定して関数の返り値の型を推論させるより、関数の返り値の方で指定したい

const fn1 = () => {
	return [
		{ foo: "bar" },
	] as Foo[]
}

const fn2 = (): Foo[] => {
	return [
		{ foo: "bar" },
	]
}

fn1 より fn2 のようにしたい
fn2 の型情報を取得するときに、 fn1 の書き方だと関数の内部まで見ないといけなくて重くなるはずだし

また、推論の方法だと、別の return も追加できて、その return が同じ型を返さなくてもエラーにならない
でも、その関数を使うところでエラーになる可能性はあって、そこでエラーになると原因が分かりづらいエラーになる
なのでどうせ書くなら関数の返り値を明示的に書く fn2 形式にしたい

ただ、 fn2 の場合で return する値を変数に入れたいとき、同じ型を複数回書くことになる

const fn3 = (): Foo[] => {
	const items: Foo[] = [
		{ foo: "bar" },
	]
	if (baz) {
		items.push({ foo: "baz" })
	}
	return items
}

const fn4 = (): Foo[] => {
	const items = [
		{ foo: "bar" },
	]
	if (baz) {
		items.push({ foo: "baz" })
	}
	return items
}

fn3Foo[] を 2 回書いてる
できれば 2 回書くのは避けたい
変更時に面倒だし、型エラーにならないレベルの修正なら修正漏れのままになる可能性もあるし
また TypeScript だとジェネリクスとか Union Type とかの組み合わせでとても長い型になることも頻繁にある
それを繰り返し書きたくない
ジェネリクスを関数風に使えば短くできなくはないけど、そのためだけにそこまではしたくない

fn4 形式で明示しなくても動くこともあるけど、 items 中の foo のプロパティが string になるので、 Foo の定義で foo"bar" | "baz" みたいな型の場合はエラーになる
また、入力中に itemFoo[] という情報がないので入力補完もされない

ということで諦めて fn1 に近い形にすることが多い
items に型を指定して、関数自体には返り値の型を指定せず推論させる

ふと「関数内で自身の返り値型取れないのかな」と思って、 ReturnType を試してみたら問題なさそうだった

const fn5 = (): Foo[] => {
	const items: ReturnType<typeof fn5> = [
		{ foo: "bar" },
	]
	if (baz) {
		items.push({ foo: "baz" })
	}
	return items
}

fn5 自体の型は fn5 への代入文の次以降で有効になりそうで、その途中である関数内で参照しようとするとエラーになりそうな気はしたけどならないみたい
さすがに Foo[] を明示的に書かずに推論させる場合は無理だったけど、明示的に書いてる場合は大丈夫らしい
この方法ありかなと思ったけど、 ReturnType の中で fn5 という名前を書かないといけないのがイヤなところ
自身の返り値の型を指す専用のキーワードとかあればいいのに

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment