※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。


【TypeScript】.tsx内でGeneric関数を定義する場合の注意点

web開発 フロント開発 TypeScript 型付き言語 TSX JSX

投稿日:2021年5月15日

このエントリーをはてなブックマークに追加
TypeScriptで便利な機能の一つにGenerics関数というものがあります。しかし、実はこのGenericsの表現は特定の条件下では言語のルール通り書いてもエラーになってしまいます。この記事ではtsxファイル内でのGenerics表現の注意点について詳しく解説しています。

はじめに

この記事について

TypeScriptで便利な機能の一つにGenerics関数というものがあります。

しかし、実はこのGenericsの表現は特定の条件下では言語のルール通り書いてもエラーになってしまいます。

この記事ではtsxファイル内でのGenerics表現の注意点について詳しく解説しています。

環境について

この記事は以下の環境で確認されました。

  • typescript version 4.2.3

実践

実際のコードを通じて一つずつ確認していきましょう。

function宣言を使った関数定義

まずはfunction宣言を使った以下のようなGenericsの関数定義があるとします。

function SampleFunc<T>(sampleValue:T){
    console.log(`value : ${sampleValue} \r\ntype : ${typeof sampleValue}`);
}

この様にfunction宣言を使った関数定義は.ts.tsxどちらに書いても問題なく動作します。

アローでGenericsの関数定義

では次に同じ機能をもつ関数をアロー関数で定義してみます。

const SampleFunc = <T>(sampleValue: T) => {
  console.log(`value : ${sampleValue} \r\ntype : ${typeof sampleValue}`);
};

コード自体は特に難しいところはありません。

この記述は.tsファイルにこのような記述をした場合には問題なく動作します。しかし、この記述を.tsxファイルに持っていった場合にはいきなりエラーになってしまいます。

発生するエラーが以下のようなものです。

JSX element 'T' has no corresponding closing tag.ts(17008)
Cannot find name 'T'

エラーの内容は以下のようなことを言ってます。

  • JSX表現のTのタグが閉じられていないよ
  • そもそもTという名前のタグが定義されていないよ

エラーの原因

JSXでは山括弧(<>)で囲まれたものをJSXのタグとして認識します。一方、TypeScript側は山括弧で(<>)囲まれたものをGeneric関数の型引数として認識します。

.tsxはTypeScript内でJSXを使える様にしたものなので、function宣言を使っていない山括弧はこの2つの定義がぶつかってしまっているということですね。
※これが放置されているのはなぜなのか…

解決方法

考え方としてはTypeScriptのGenericsの型引数の表現にはあってJSXのタグ表現にはない書き方をすると型引数として認識してくれます。

ここには2つ解決方法を書いておきます

1.カンマを使う

TypeScriptのGeneric関数の型引数はカンマで区切ることで複数の型を使うことができます。JSXにはタグ表現にカンマを含めることができないため、型引数として認識してくれます。

index.tsx
const SampleFunc = <T, K>(sampleValue: T, sampleKey: K) => {
  console.log(`value : ${sampleValue} \r\nkey : ${sampleKey}`);
};

TypeScriptではトレイリングカンマが許可されているため、以下のように書くことで一つしかない型引数も問題なく記述できます。

index.tsx
const SampleFunc = <T,>(sampleValue: T) => {
  console.log(`value : ${sampleValue} \r\ntype : ${typeof sampleValue}`);
};

2.extendsを使う

TypeScriptはextendsで型引数の型に制限をつけることができます。当然JSXにこのような表現はないため型引数として認識してくれます。

index.tsx
const SampleFunc = <T extends string>(sampleValue: T) => {
  console.log(`value : ${sampleValue} \r\ntype : ${typeof sampleValue}`);
};

ここではTstringをの定義を含んでいるという制限をつけました。

しかしこれはTに制限をつけてしまっているため、元の関数定義と異なっています。元の関数と同じ動作にさせたい場合には、『型の制限自体はあるが一切意味をなさない』という状況を作ってあげればOKです!typescriptの型には継承関係があり、その最もルートに位置するのがunknownという型です。すべての型は必ずunknownを継承しているため以下のように書くことでTの制限を実質無いものとすることができます。

index.tsx
const SampleFunc = <T extends unknown>(sampleValue: T) => {
  console.log(`value : ${sampleValue} \r\ntype : ${typeof sampleValue}`);
};

さいごに

感想

アロー関数でGenerics書くたびに謎な表記を書きたくないので早くデフォルトの対応を入れてほしいですね〜

このエントリーをはてなブックマークに追加


関連記事

記事へのコメント