Typescriptの型指定とReactでの型定義 簡易版 @yukilulu0229

## Typescript

### 基本的な型

```TypeList.ts

// boolean
let bool: boolean = true;
console.log(bool);
// number
let num:number = 0;
console.log(num);
//string
let str:string = "A";
// Array
let arr1:Array<number> = [0, 1, 2];
console.log(arr1);
let arr2:number[] = [3, 4, 5];
console.log(arr2);
// tuple
let tuple: [number, string] = [0, "A"];
console.log(tuple);
// any
let any1: any = false;
console.log(any1);
// void 戻り値がない
const funcA = (): void => {
  const test = "text";
  console.log(test);
};
funcA();
// null
let null1: null = null;
console.log(null1)
// undefined
let undefined1:undefined1 = undefined
console.log(undefined1);
// object
let obj1: object = {};
console.log(obj1);
let obj2: {id: number, name: string} = {id: 1, name: "A"};
console.log(obj2);

```

### 引数の型指定

```Argument.tsx
import React from 'react';

const Argument = () => {
  const calcTotalFee = (num:number):void => {
    const total:number = num * 1.1;
    console.log(total);
  }
  const onClickPractice = ():void => {
    calcTotalFee(1000);
  };
  return (
    <div>
      <p>引数の型指定</p>
      <button onClick={onClickPractice}>練習問題1</button>
    </div>
  );
};

export default Argument;

```

## Reactに型定義

### 型管理

```types/todo.ts
export type TodoType = {
  userId: number,
  id: number,
  title: string,
  completed: boolean
}
```

```types/user.ts
export type User = {
  name: string,
  hobbies?: Array<string>
}
```

```
hobbies?: Array<string>
```
hobbies"?"は入れがない場合を許容する


### コンポーネント

#### App.tsx 
```App.tsx
import './App.css'
import axios from "axios";
import {useState} from "react";
import Todo from "./Todo.tsx";
import {TodoType} from "./types/todo.ts";
import Text from "./Text.tsx";
import {User} from "./types/user.ts";
import UserProfile from "./UserProfile.tsx";

// type TodoType = {
//   userId: number,
//   id: number,
//   title: string,
//   completed: boolean
// }

const user:User = {
  name: "yukilulu0229",
  hobbies: ["Guitar", "Programing", "React"]
}

function App() {
  const [todos, setTodos] = useState<Array<TodoType>>([])
  const onClickFetchData = () => {
    axios.get<Array<TodoType>>("https://jsonplaceholder.typicode.com/todos/").then((res) => {
      // console.log(res.data.userId)
      setTodos(res.data);
    })
  };
  return (
    <>
      <button onClick={onClickFetchData}>データ取得</button>
      {todos.map((todo) => (
        <Todo key={todo.id} title={todo.title} userId={todo.userId} completed={todo.completed} />
      ))}
      <Text color={"red"} fontSize={"44px"}/>
       <UserProfile user={user}/>
    </>
  )
}

export default App


```

```
const [todos, setTodos] = useState<Array<TodoType>>([])
```

- [todos, setTodos]なので Array
- TodoTypeはtypes/todo.tsから読み込んでる

```
axios.get<Array<TodoType>>
```
APIが配列型だからArray

```
<Todo key={todo.id} title={todo.title} userId={todo.userId} completed={todo.completed} />
```

Todoコンポーネントに渡したいもの

```
const user:User = {
  name: "yukilulu0229",
  hobbies: ["Guitar", "Programing", "React"]
}
```

Userはtypes/user.tsからインポートしている

```
<UserProfile user={user}/>
```

userのオブジェクトごと渡している


#### Todo.tsx

```Todo.tsx
import React, {FC} from 'react';
import {TodoType} from "./types/todo.ts";

// type TodoType = {
//   userId: number,
//   title: string,
//   completed?: boolean // ?をつけるとundefinedも許容
// }

// const Todo = (props: Pick<TodoType, "userId" | "title" | "completed">) => {
// const Todo = (props: Omit<TodoType, "id">) => {
const Todo: FC<Omit<TodoType, "id">> = (props ) => {
  const {title, userId, completed = false} = props;
  const completedMark:string = completed ? "[完了]" : "[未完了]"
  return (
    <div>
      <p>{`${completedMark}${title} (user: ${userId})`}</p>
    </div>
  );
};

export default Todo;


```

```
import {TodoType} from "./types/todo.ts";
```
todo.tsの型定義をimport

```
const Todo = (props: Pick<TodoType, "userId" | "title" | "completed">) => {
```

PickでTodoTypeの有効化する型定義

```
const Todo = (props: Omit<TodoType, "id">) => {
```

OmitでTodoTypeの無効化する型定義

```
const Todo: FC<Omit<TodoType, "id">> = (props ) => {
```

FCは関数型コンポーネントに使う

#### Text.tsx

```Text.tsx
import React, {FC} from 'react';

type Props = {
  color: string,
  fontSize: string,
}

// 関数型コンポーネント場合はFC
const Text: FC<Props> = (props) => {
  const {color, fontSize} = props
  return (
    <div>
      <p style={{color, fontSize}}>Text file</p>
    </div>
  );
};

export default Text;

```

```
const Text: FC<Props> = (props) => {
```
FCは関数型コンポーネントに使う


```UserProfile.tsx
import React, {FC} from 'react';
import {User} from "./types/user.ts";

type Props = {
  user: User
}

const UserProfile: FC<Props> = (props) => {
  const {user} = props;
  return (
    <dl>
      <dt>名前</dt>
      <dd>{user.name}</dd>
      <dt>趣味</dt>
      <dd>{user.hobbies?.join(" / ")}</dd>
    </dl>
  );
};

export default UserProfile;


```
```
type Props = {
  user: User
}
```

Userをインポートして型定義している

```
<dd>{user.hobbies?.join(" / ")}</dd>
```

hobbies?でnullやundefinedば場合は後ろの .joinを実行しない
オプショナルチェインという

### ライブラリの型定義

```package.json
{
  "name": "react_jakey_02",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.6.7",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@types/react": "^18.2.43",
    "@types/react-dom": "^18.2.17",
    "@typescript-eslint/eslint-plugin": "^6.14.0",
    "@typescript-eslint/parser": "^6.14.0",
    "@vitejs/plugin-react": "^4.2.1",
    "eslint": "^8.55.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.5",
    "typescript": "^5.2.2",
    "vite": "^5.0.8"
  }
}


```

```
"@types/react-dom": "^18.2.17",
```
などは型定義されているない場合は
@types/foobarを探す
GitHubをみてfoobar.d.tsがあれば型定義されていることが多い