🕌

【TypeScript】いろんな型定義を調べてみた

2023/03/05に公開

概要

最近、React×TypeScriptの案件であったけど、型を指定する際のパターンをメモ。
特にもReactの場合って通常のstring型とか以外にもある訳で。
ネットで検索していろいろ調べてたので忘備録としてメモ。

Propsに型を定義する

関数コンポーネントの引数Propsの型を定義するには、次のようにします。

propsで受け取る引数がひとつの場合

よくpropsで値を受けとる先に分割代入をすることがあるが、その型指定の方法。

//直接指定する
const App=({title}:{title:string})=>{}

//型エイリアス
type Props={title:string}
const App=({title}:Props)=>{}

型を直接指定する方法と型エイリアスを使う方法。

propsで受け取る引数が複数の場合

複数の場合は、直接型指定するより型エイリアスの方が良い。

//型エイリアス
type Props={
 title:string,
 num:number,
 bool:boolean
}
const App=({title,name,bool}:Props)=>{}

childenを型指定する場合

childrenはコンポーネントタグで囲った場合にどこに表示させるか指定する際に利用する。
childrenはJSX要素を受けとる場合にReactNodeを指定して、単純に文字列だけの場合などはその型を指定する。
ReactNodeは下記のようにUnion型で指定されてる。

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

実際は以下のような感じ。

//コンポーネント
const Contents=({children}:{children:React.ReactNode})=>{
  return(
	<div>
	<p>ここはContestsコンポーネント</p>
	{children}
	</div>
  )
}

//呼び出す
const App=()=>{
   return(
     <div>
       <p>ここは呼び出し元</p>
        <Contents>
	  ここに内容を表示
	</Contents>
     </div>
  )
}

文字列だけの場合

//コンポーネント
const Button=({children}:{children:string})=>{
  return(
	<button>{children}</button>
  )
}

//呼び出す
const App=()=>{
   return(
     <div>
       <p>ここは呼び出し元</p>
        <Button>クリック</Button>
     </div>
  )
}

propsに渡す関数がある場合

関数などを親コンポーネントから渡す場合もあるので、その型の定義方法。
型エイリアスで書いた方が見通し良いと思うので型エイリアス。
引数や戻り値ありの場合もちゃんと記述しないといけない。

//引数や戻り値がある場合は以下
type Props={
  sampleFunc:(text:string)=>string; //引数、戻り値がstring型
  children:React.ReactNode:
}
//戻り値や引数がない場合は以下
type Props={
  sampleFunc:()=>void; //戻り値がない場合はvoid
  children:React.ReactNode:
}

const BtnComp=({sampleFunc,children}:Props)=>{
  return(
	<>
	<button onClick={()=> alert(returnFc("テスト"))}>{children}</button>
	</>
  )
}

//呼び出す
const App=()=>{
  //BtnCompに渡す関数
  const sampleFunc = (text: string) => "出力:" + text;
  
   return(
     <div>
       <p>ここは呼び出し元</p>
        <BtnComp sampleFunc={sampleFunc}>アラート</BtnComp>
     </div>
  )
}

関数コンポーネントの型React.FCでの指定方法

Reactの場合、関数コンポーネントでReact.FCという型がある。
こういった記述方法もある。

const App: React.FC = () => {
  return (
    <div>
    </div>
  );
};

型エイリアスをジェネリクスで渡す方法

type User={
  username:string
}

const App: React.FC<User> = ({username}) => {
  return (
    <div>
     <p>{username}</p>
    </div>
  );
};

上記のようにReact.Fc<User>としてジェネリクスで渡している。
なお、childrenの場合は下記になる。

type Props={
  children: React.ReactNode
}

const App: React.FC<Props> = ({children}) => {
  return (
    <div>
      {children}
    </div>
  );
};

関数を渡す場合も同じくジェネリクスで渡すと下記のようになる。

type ClickButton={
 clickHandler:()=> void,
 children: React.ReactNode;
}

const ClickBtn: React.FC<ClickButton> = ({clickHandler, children }) => {
  return (
    <div>
      <button onClick={() => clickHandler()}>{children}</button>
    </div>
  );
};


//呼び出す
const App:React.FC=()=>{

const clickHandler = ()=>{
 console.log("click")
}
   return(
     <div>
       <p>ここは呼び出し元</p>
        <ClickBtn clickHandler={clickHandler}>クリック</ClickBtn>
     </div>
  )
}

「?」で存在しない値を許容する

型を指定する際に、ある値がなくても問題ないという事を表すため為に「?(はてな)」をつける事ができる。これはundefinedの可能性もあるので、それを許容するという意味にもなる。

type Props={
 name:string,
 email?:string
}
const Contents=({name,email}:Props)=>{
  return(
     <div>
	<p>私の名前は{name}です。</p>
	{email && (
        <p>email:{email}</p>
      )}
    </div>
  )
}

上記の例でいうと、nameは必須だけどもemail?:stringとなっているのでemailに関してはundefinedを許容しているという事になる。
その場合、emailの型はstring | undefinedというユニオン型になる。
ただしemailの場合は、単純に出力しようとすると値が存在しない(undefined)の可能性もあるので、&&条件で存在しているか確認してから出力する。

デフォルト値を設定しておく

上記のように値がstring | undefinedを許容していても値が無かった場合の値を決めておきたい時に以下のように指定もいい。
仮にnameの値もstring | undefinedを許容して初期値を設定する。

type Props={
 name?:string,
 email?:string
}
const Contents=({name="ゲスト",email}:Props)=>{
  return(
     <div>
	<p>私の名前は{name}です。</p>
	{email && (
        <p>email:{email}</p>
      )}
    </div>
  )
}

上記にようにnameの値がない場合は、「ゲスト」の値が入る。

ユニオン型についての注意点 - 戻り値

string | undefinedで許容したとしても、関数で処理した結果を戻す場合に気を付けておいた方がいいと思った事。

例えば、以下の例はエラーになる。

const sampleFunc=(name?:string)=>{
 return name;
}

const resultName:string=sampleFunc();

なぜエラーになるかというと、string | undefined のユニオン型が定義されているのに戻り値で受ける変数が文字列(string)型のためエラーになる

ちなみのlocalStorage.getItem()の場合もエラーがでました。

const maybeUserId: string | null = localStorage.getItem("userId");
const userId: string = maybeUserId; // nullかもしれないので、代入できない。

以下のようなエラーがでる。

解決策としては条件分岐や!(感嘆符)で対処する
localStorage.getItem()のような元からある場合はif文や&&条件といった条件分岐、
自作関数なら「!(感嘆符)」などで対処。

//条件分岐
const maybeUserId: string | null = localStorage.getItem("userId");
if (typeof maybeUserId === "string") { //文字列型かチェック
  const userId: string = maybeUserId;
}

//感嘆符
const sampleFunc=(name?:string)=>{
 return name!;
}

const resultName:string=sampleFunc();

感嘆符に関しては参考にしたブログの引用ですが、下記の説明でした。

name?の型は暗黙的に string | undefined のユニオン型が定義されているので、文字列型あ>る変数resutNameの型と一致しない訳エラーになります。
単純にresutNameの型をstring | undefinedにすれば良さそうですが、他にも戻り値で返す
nameの後ろに「!」(感嘆符)をつけることでもエラーが解消します。
多分ですが、undefinedでないことを宣言しているので、文字列型として処理されているのだ > と思います。

配列関連

配列の指定方法もいろいろあるのでチェックしてみた。
まずは型の後ろに配列の[]を付ける。

const strArr=string[] =["文字列1","文字列2"]; //文字列型の配列
const numArr=number[]=[1,2,3]; //数字型の配列

//ユニオン型で複数の型の配列も可能
const arrStrNum=(string | number)[] = ["文字列",1];

ジェネリクス型で定義

ジェネリクス型でも指定できる。Array<型>型で定義する。

const strArr=Array<string> =["文字列1","文字列2"]; //文字列型の配列
const numArr=Array<number>=[1,2,3]; //数字型の配列

//ユニオン型で複数の型の配列も可能
const arrStrNum=Array<string | number>[] = ["文字列",1];

配列の中にオブジェクトがある場合

よくデータ取ってくる際に、配列の中にオブジェクトがあったりするので、その場合の型の指定方法。こちらも通常の配列と同様に2種類の指定方法。

type PersonalInfo={
 name:string,
 age:number,
 gender:string
}

const Persons = PersonalInfo[]=[
 {name:"名前",age:25,gender:"男"},
 {name:"花子",age:22,gender:"女"},
]

//ジェネリクス型だと下記
const Persons = Array<PersonalInfo>=[
 {name:"名前",age:25,gender:"男"},
 {name:"花子",age:22,gender:"女"},
]

Event関連

クリックイベントなど、Reactだと自動的にany型になっていたりする。

const onClick = (event: any) => {
    //処理
  };

イベントにはchangeイベントやfocusイベントなど色々あるんですが、以下のようなイベントの型がある。typeにまとめてあります。

  const onClick = (e: React.MouseEvent<HTMLInputElement>) => {
  }
  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  }
  const onkeypress = (e: React.KeyboardEvent<HTMLInputElement>) => {
  }
  const onBlur = (e: React.FocusEvent<HTMLInputElement>) => {
  }
  const onFocus = (e: React.FocusEvent<HTMLInputElement>) => {
  }
  const onSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  }
  const onClickDiv = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
  }
  const onClickButton = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
  }

VScode(Visual Studio Code)を利用している場合は、マウスオーバーすればイベントの型を表示してくれます。

マウスオーバーしたらそれをコピーして、以下のようにすればいいかと。

  const onClickhandler=(e: React.MouseEvent<HTMLButtonElement, MouseEvent>)=>{
    //処理
  }

  return (
    <div className="App">
     <button onClick={(e)=>onClickhandler(e)}>クリック</button>
    </div>
  );
}

useState関連

ReactHooksのuseStateに関しては、useStateフックでは初期値を与えれば状態変数の型を推論してくれますが、あまり好ましくないよう。

型推論

const [name, setName] = useState("")  // string型
const [count, setCount] = useState(0) // number型

ジェネリクス型

useStateは基本的にはジェネリクス型で対応した方がいい。

const [name, setName] = useState<string>("")           // string型
const [count, setCount] = useState<number>(0)          // number型
const [isChecked, setIsChecked] = useState<boolean>(false)   // boolean型

配列やオブジェクトが含まれる場合

よくjsonからデータを取ってくると大抵はオブジェクト型だったりしてそれを配列で受け取ったりするのが多い。そのような場合は以下のようにする。
例えばユーザー情報を取ってくる場合など。

type User={
 id:number
 name:string,
 gender:string
}

const [user, setUser] = useState<User[]>([{id:0,name:"名前","男"}])  

まとめ

下記のサイトを参考にさせていただきました。
https://m-kenomemo.com/typescript-basic/
https://mo-gu-mo-gu.com/create-react-app-typescript/
https://zenn.dev/kenta0313/articles/a39fb1d8edc3a4
https://qiita.com/hinako_n/items/97ccaf85eb40d7e45657
https://qiita.com/Takepepe/items/f1ba99a7ca7e66290f24

Discussion