TypeScriptを試してみたいけど実際どう書くのかイマイチよくわからない人に捧ぐ。
fwywd一次採用試験内容の一部をお借りして、実際に実装してみる。
筆者もTypeScript初心者だから、もっと良い書き方があるかもしれない。
アドバイスがあればぜひよろしくね。
こんな人を対象にしているよ
- fwywd一次採用試験にTypeScriptを使って挑戦したい
- ReactやNext.jsでTypeScriptを使用したい
- Next.js+TypeScriptでコンポーネントを作りたい
けど、書き方がよくわからない人。
想定している前提知識
以下の知識があれば「なんじゃこりゃ」とならず読めるよう想定して執筆している。
- Reactで親から子へ値を渡す方法(Props)
- Next.jsの基本知識
- Tailwind CSSの基本知識
この記事で触れない部分
長くなるのでここでは割愛する。
- TypeScriptは何か
- Next.js + TypeScript + Tailwind CSSのプロジェクト作成方法
参考リンク集:
【入門】TypeScriptとは?言語の特徴やJavaScriptとの違いを解説
Next.js (JavaScript/TypeScript) に Tailwind CSS を導入する方法
バージョン情報
next: 11.0.1
typescript:4.3.5
react: 17.0.2
react-dom: 17.0.2
完成物
コンポーネント「ProgressBar.tsx」を作って3つ並べてみる。
拡大Ver
ファイル構造
使いやすいようにディレクトリ構造をデフォルトから下記へ変更している。
ディレクトリ構造の変更方法を知りたかったらこの記事が非常に参考になる。
またディレクトリ構造の変更に伴いbaseUrlとpathsをtsconfig.jsonに登録してある。
{
"compilerOptions": {
"baseUrl": "src",
"paths": {
"@/components/*": ["components/*"]
}
}
}
全コード
src/pages/skills.tsx
import { ProgressBar, Props } from '@/components/Shared/ProgressBar';
const skill1: Props = {
name: 'Next.js',
percentWidth: 'w-3/5',
percent: 60,
};
const skill2: Props = {
name: 'TypeScript',
percentWidth: 'w-4/5',
percent: 80,
};
const skill3: Props = {
name: 'Tailwind CSS',
percentWidth: 'w-4/5',
percent: 80,
};
const skills: Props[] = [skill1, skill2, skill3];
export default function Skills(): JSX.Element {
return (
<section
id='skills'
className='flex justify-center items-center py-7 w-screen h-screen bg-green-100'
>
<div className='p-4 bg-white rounded-md'>
{skills.map((skill, index) => {
return (
<div className='mb-2' key={index}>
<ProgressBar
name={skill.name}
percentWidth={skill.percentWidth}
percent={skill.percent}
/>
</div>
);
})}
</div>
</section>
);
}
src/components/ProgressBar.tsx
export interface Props {
name: string;
percentWidth: string;
percent: number;
}
export const ProgressBar = ({ name, percentWidth, percent }: Props): JSX.Element => {
return (
<div className='relative'>
<div className='flex items-center'>
<div>
<span className='inline-block text-sm font-semibold tracking-wider text-text-green uppercase'>
{name}
</span>
</div>
</div>
<div className='grid grid-cols-10 text-left'>
<div className='flex overflow-hidden col-span-8 mt-2 h-3 text-xs bg-bar-lightgreen rounded'>
<div
className={`${percentWidth} flex flex-col justify-center text-center text-white whitespace-nowrap bg-bar-green shadow-none`}
></div>
</div>
<div className='col-span-2 ml-3'>
<span className='text-sm font-semibold text-text-green lg:text-base'>{percent}%</span>
</div>
</div>
</div>
);
};
手順1. ProgressBar.tsxの作成
まずコンポーネントを作る。
test/src/componentsに新しいファイル「ProgressBar.tsx」を作成する。
export interface Props {
name: string;
percentWidth: string;
percent: number;
}
export const ProgressBar = ({ name, percentWidth, percent }: Props): JSX.Element => {
return (
<div className='relative'>
<div className='flex items-center'>
<div>
<span className='inline-block text-sm font-semibold tracking-wider text-text-green uppercase'>
{name}
</span>
</div>
</div>
<div className='grid grid-cols-10 text-left'>
<div className='flex overflow-hidden col-span-8 mt-2 h-3 text-xs bg-bar-lightgreen rounded'>
<div
className={`${percentWidth} flex flex-col justify-center text-center text-white whitespace-nowrap bg-bar-green shadow-none`}
></div>
</div>
<div className='col-span-2 ml-3'>
<span className='text-sm font-semibold text-text-green lg:text-base'>{percent}%</span>
</div>
</div>
</div>
);
};
interfaceの作成
export interface Props {
name: string;
percentWidth: string;
percent: number;
}
ページコンポーネントのskills.tsxでも使うので「export」を忘れずに。
export const ProgressBar = ({ name, percentWidth, percent }: Props): JSX.Element => {
return (
...
「name, percentWidth, percent」をPropsとして受け取る。型はinterface Propsを参照しているため、違うプロパティ名を書くと怒られる。
上の図では「namaeeeなんてプロパティはinterface Propsの中にないよ!」って怒られている。
表示部分の実装
受け取ったPropsはそれぞれ以下のように反映させている。
export const ProgressBar = ({ name, percentWidth, percent }: Props): JSX.Element => {
return (
<div className='relative'>
<div className='flex items-center'>
<div>
<span className='inline-block text-sm font-semibold tracking-wider text-text-green uppercase'>
{name}
</span>
</div>
</div>
<div className='grid grid-cols-10 text-left'>
<div className='flex overflow-hidden col-span-8 mt-2 h-3 text-xs bg-bar-lightgreen rounded'>
<div
className={`${percentWidth} flex flex-col justify-center text-center text-white whitespace-nowrap bg-bar-green shadow-none`}
></div>
</div>
<div className='col-span-2 ml-3'>
<span className='text-sm font-semibold text-text-green lg:text-base'>{percent}%</span>
</div>
</div>
</div>
);
};
受け取ったPropsをclassNameに反映させる時の書き方が少々特殊なので注意。間違えると反映されない。
/*わかりやすいようにCSS記述を一部省略*/
<div className={`${percentWidth} flex flex-col`}></div>
手順2. skills.tsxの作成
test/src/pagesに新しいファイル「skills.tsx」を作成する。
import { ProgressBar, Props } from '@/components/Shared/ProgressBar';
const skill1: Props = {
name: 'Next.js',
percentWidth: 'w-3/5',
percent: 60,
};
const skill2: Props = {
name: 'TypeScript',
percentWidth: 'w-4/5',
percent: 80,
};
const skill3: Props = {
name: 'Tailwind CSS',
percentWidth: 'w-4/5',
percent: 80,
};
const skills: Props[] = [skill1, skill2, skill3];
export default function Skills(): JSX.Element {
return (
<section
id='skills'
className='flex justify-center items-center py-7 w-screen h-screen bg-green-100'
>
<div className='p-4 bg-white rounded-md'>
{skills.map((skill, index) => {
return (
<div className='mb-2' key={index}>
<ProgressBar
name={skill.name}
percentWidth={skill.percentWidth}
percent={skill.percent}
/>
</div>
);
})}
</div>
</section>
);
}
上から順に解説していく。
ProgressBar.tsxのインポート
まず手順1で作ったプログレスバーを使えるようインポートする。
import { ProgressBar, Props } from '@/components/Shared/ProgressBar';
interface Propsもインポートしているところがポイント。再利用することでコードの記述量を減らせる。
定数の作成
プログレスバーの中身を書いていく。3つバーを作って並べる予定だから3つ作る。
import { ProgressBar, Props } from '@/components/Shared/ProgressBar';
const skill1: Props = {
name: 'Next.js',
percentWidth: 'w-3/5',
percent: 60,
};
const skill2: Props = {
name: 'TypeScript',
percentWidth: 'w-4/5',
percent: 80,
};
const skill3: Props = {
name: 'Tailwind CSS',
percentWidth: 'w-4/5',
percent: 80,
};
percentWidthの「w-3/5 (w-4/5)」はTailwind CSS由来のクラス名。こうやって書くだけでwidthを60%(と80%)にしてくれるスグレモノ。
エラー例その1
ProgressBar.tsxからインポートしてきたinterface Propsを型定義として使っているから、
違う型で書くと怒られる。
「percentはnumberって定義したじゃん!stringで書かないで!」と怒られている図。
const skill2のpercentは数字で書いてるから怒られていない。
エラー例その2
interface Propsの中に存在しないプロパティを書いても怒られる。
「interface Propsの中にそんなプロパティは存在しないでしょ!」と怒られている。
インターフェースProps配列の作成
プログレスバー3つをmap関数でかっこよく表示するため配列を作る。
const skills: Props[] = [skill1, skill2, skill3];
string[]やnumber[]みたいにinterface配列Props[]も作れちゃうのが嬉しいところ。
表示部分の実装
ProgressbarにPropsを渡して表示。
export default function Skills(): JSX.Element {
return (
<section
id='skills'
className='flex justify-center items-center py-7 w-screen h-screen bg-green-100'
>
<div className='p-4 bg-white rounded-md'>
{skills.map((skill, index) => {
return (
<div className='mb-2' key={index}>
<ProgressBar
name={skill.name}
percentWidth={skill.percentWidth}
percent={skill.percent}
/>
</div>
);
})}
</div>
</section>
);
}
コードの記述量が減ってカッコよくなるのでmap関数を使用している。
map関数を使うときはkeyを記述しないと動かないので注意。
ここでは配列のインデックスをkeyとして使用している。
ユニークな(他と被らない)値であればなんでもいいのでkey={skill.name}とかにしてもOK。
{skills.map((skill, index) => {
return (
<div className='mb-2' key={index}>
<ProgressBar
name={skill.name}
percentWidth={skill.percentWidth}
percent={skill.percent}
/>
</div>
);
})}
これでおしまい。
おわりに
ここまで読んでくれてありがとう。
ちょっとでも参考になったら「いいね!」を押してオラにモチベーションを与えてくれると嬉しいな。
他の記事も良かったら読んでみてね。
【Git Flow】チーム開発のお作法~GitHubとブランチ管理編【採用試験対策】
【Next.js+TypeScript】Tailwind CSSの環境構築Q&A
【Next.js+TypeScript】fwywdの一次採用試験攻略ガイド~試験の流れ編【採用試験対策】