├── .prettierrc
├── .gitignore
├── src
├── basic
│ ├── index.ts
│ ├── any.ts
│ ├── unknown.ts
│ ├── primitive.ts
│ └── notExist.ts
├── world.ts
├── array
│ ├── tuple.ts
│ └── array.ts
├── generics
│ ├── advanced.ts
│ └── basic.ts
├── function
│ ├── parameters.ts
│ └── basic.ts
├── asynchronous
│ ├── callback.ts
│ ├── promise.ts
│ └── asyncAwait.ts
├── object
│ ├── object.ts
│ └── alias.ts
├── interface
│ └── interface.ts
├── index.ts
└── oop
│ └── shogi.ts
├── dist
├── index.html
└── bundle.js
├── .eslintrc.js
├── webpack.config.js
├── package.json
├── LICENSE
├── README.md
└── tsconfig.json
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "printWidth": 120,
3 | "singleQuote": true,
4 | "semi": false
5 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # packages
2 | node_modules/
3 | package-lock.json
4 |
5 | # local files
6 | .idea/
7 | .DS_Store
8 | *.log
9 |
--------------------------------------------------------------------------------
/src/basic/index.ts:
--------------------------------------------------------------------------------
1 | export { default as anySample } from './any'
2 | export { default as notExist } from './notExist'
3 | export { default as primitiveSample } from './primitive'
4 | export { default as unknownSample } from './unknown'
5 |
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 日本一わかりやすいTypeScript入門
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/basic/any.ts:
--------------------------------------------------------------------------------
1 | export default function anySample() {
2 | let name: any = 'Torahack' // string型を代入
3 | console.log('any sample 1:', typeof name, name)
4 |
5 | name = 28 // number型を再代入できる
6 | console.log('any sample 2:', typeof name, name)
7 | }
8 |
--------------------------------------------------------------------------------
/src/world.ts:
--------------------------------------------------------------------------------
1 | export default class World {
2 | // クラスで使うプロパティ
3 | message: string
4 |
5 | // コンストラクタ(初期化)
6 | constructor(message: string) {
7 | this.message = message
8 | }
9 |
10 | // 外部から呼び出せるメソッドを定義
11 | public sayHello(elem: HTMLElement | null) {
12 | if (elem) {
13 | return (elem.innerText = this.message)
14 | } else {
15 | return
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/array/tuple.ts:
--------------------------------------------------------------------------------
1 | export default function tupleSample() {
2 | // 一般的なタプルの型定義
3 | const response: [number, string] = [200, 'OK']
4 | // response = [400, "Bad Request", "Email parameter is missing"] // 定義された要素数と合わない
5 | // response = ["400", "Bad Request"] // numberにstringを代入できない
6 | console.log('Array tuple sample 1:', response)
7 |
8 | // 可変長引数を使ったタプル
9 | const girlfriends: [string, ...string[]] = ['Kana', 'Miku', 'Keiko']
10 | console.log('Array tuple sample 2:', girlfriends)
11 | }
12 |
--------------------------------------------------------------------------------
/dist/bundle.js:
--------------------------------------------------------------------------------
1 | (()=>{"use strict";var e={607:function(e,t,r){var o=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var n=o(r(54)),u=document.getElementById("root");new n.default("Hello World!!!").sayHello(u)},54:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.message=e}return e.prototype.sayHello=function(e){e&&(e.innerText=this.message)},e}();t.default=r}},t={};!function r(o){if(t[o])return t[o].exports;var n=t[o]={exports:{}};return e[o].call(n.exports,n,n.exports,r),n.exports}(607)})();
--------------------------------------------------------------------------------
/src/basic/unknown.ts:
--------------------------------------------------------------------------------
1 | export default function unknownSample() {
2 | const maybeNumber: unknown = 10 // unknown
3 | console.log('unknown sample 1:', typeof maybeNumber, maybeNumber)
4 |
5 | // unknown型の値を比較することができる
6 | const isFoo = maybeNumber === 'foo'
7 | console.log('unknown sample 2:', typeof isFoo, isFoo)
8 |
9 | // const sum = maybeNumber + 10 // object is possibly unknownのエラーが出て代入できない
10 |
11 | // number型であるか確認した後なら代入可能
12 | if (typeof maybeNumber === 'number') {
13 | const sum = maybeNumber + 10
14 | console.log('unknown sample 3:', typeof sum, sum)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true
5 | },
6 | extends: [
7 | "eslint:recommended",
8 | "plugin:@typescript-eslint/recommended", // TypeScriptでチェックされる項目をLintから除外する設定
9 | "prettier", // prettierのextendsは他のextendsより後に記述する
10 | "prettier/@typescript-eslint",
11 | ],
12 | plugins: ["@typescript-eslint"],
13 | parser: "@typescript-eslint/parser",
14 | parserOptions: {
15 | "sourceType": "module",
16 | "project": "./tsconfig.json" // TypeScriptのLint時に参照するconfigファイルを指定
17 | },
18 | root: true, // 上位ディレクトリにある他のeslintrcを参照しないようにする
19 | rules: {}
20 | }
--------------------------------------------------------------------------------
/src/basic/primitive.ts:
--------------------------------------------------------------------------------
1 | export default function primitiveSample() {
2 | const name = 'Torahack'
3 | // name = 123 // numberの値を再代入しようとするとエラーになる
4 | console.log('primitive sample 1:', typeof name, name)
5 |
6 | const age = 28
7 | // age = "28" // stringの値を再代入しようとするとエラーになる
8 | console.log('primitive sample 2:', typeof age, age)
9 |
10 | const isSingle = true
11 | // isSingle = "foo" // stringの値を再代入しようとするとエラーになる
12 | console.log('primitive sample 3:', typeof isSingle, isSingle)
13 |
14 | // 判定式の結果も代入できる
15 | const isOver20: boolean = age > 20
16 | console.log('primitive sample 4:', typeof isOver20, isOver20)
17 | }
18 |
--------------------------------------------------------------------------------
/src/basic/notExist.ts:
--------------------------------------------------------------------------------
1 | export default function notExistSample() {
2 | let name = null
3 | console.log('notExist sample 1:', typeof name, name)
4 |
5 | name = 'トラハック'
6 | if (name) {
7 | console.log('notExist sample 2:', typeof name, '吾輩は猫である。名前は' + name)
8 | } else {
9 | console.log('notExist sample 3:', '吾輩は猫である。名前はまだ' + name)
10 | }
11 |
12 | let age = undefined
13 | console.log('notExist sample 4:', typeof age, age)
14 |
15 | age = 28
16 | // if (typeof age !== 'undefined') { // グローバル変数のチェックはこの判定方法
17 | if (age) {
18 | // この判定方法ならageがnullかundefinedの両方をチェックできる
19 | console.log('notExist sample 5:', typeof age, '年齢は' + age + 'です。')
20 | } else {
21 | console.log('notExist sample 6:', '年齢は秘密です。')
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/generics/advanced.ts:
--------------------------------------------------------------------------------
1 | export default function advancedSample() {
2 | // map関数のシグネチャ
3 | type Map = (array: T[], fn: (item: T) => U) => U[]
4 |
5 | const mapStringsToNumbers: Map = (array, fn) => {
6 | const result = []
7 | for (let i = 0; i < array.length; i++) {
8 | result[i] = fn(array[i])
9 | }
10 | return result
11 | }
12 |
13 | const numbers = mapStringsToNumbers(['123', '456', '001'], (item) => Number(item))
14 | console.log('Generics advanced sample 1:', numbers)
15 |
16 | const mapNumbersToStrings: Map = (array, fn) => {
17 | const result = []
18 | for (let i = 0; i < array.length; i++) {
19 | result[i] = fn(array[i])
20 | }
21 | return result
22 | }
23 |
24 | const strings = mapNumbersToStrings(numbers, (item) => String(item))
25 | console.log('Generics advanced sample 2:', strings)
26 | }
27 |
--------------------------------------------------------------------------------
/src/function/parameters.ts:
--------------------------------------------------------------------------------
1 | // オプションパラメーターを持つ関数の例
2 | export const isUserSignedIn = (userId: string, username?: string): boolean => {
3 | if (userId === 'ABC') {
4 | console.log('Function parameters sample 1: User is signed in! Username is', username)
5 | return true
6 | } else {
7 | console.log('Function parameters sample 2: User is not signed in')
8 | return false
9 | }
10 | }
11 |
12 | // デフォルトパラメーターを持つ関数の例
13 | export const isUserSignedIn2 = (userId: string, username = 'NO NAME'): boolean => {
14 | if (userId === 'ABC') {
15 | console.log('Function parameters sample 3: User is signed in! Username is', username)
16 | return true
17 | } else {
18 | console.log('Function parameters sample 4: User is not signed in')
19 | return false
20 | }
21 | }
22 |
23 | // レストパラメーターを持つ関数の例
24 | export const sumProductsPrice = (...productsPrice: number[]): number => {
25 | return productsPrice.reduce((prevTotal, productPrice) => {
26 | return prevTotal + productPrice
27 | }, 0)
28 | }
29 |
--------------------------------------------------------------------------------
/src/asynchronous/callback.ts:
--------------------------------------------------------------------------------
1 | export default function callbackSample() {
2 | const url = 'https://api.github.com/users/deatiger'
3 |
4 | type Profile = {
5 | login: string
6 | id: number
7 | }
8 |
9 | type FetchProfile = () => void
10 |
11 | // コールバックで呼び出す非同期関数(fetch)
12 | const fetchProfileCallback: FetchProfile = () => {
13 | fetch(url)
14 | .then((res) => {
15 | // レスポンスbodyをJSONとして読み取った結果を返す
16 | res
17 | .json()
18 | .then((json: Profile) => {
19 | console.log('Asynchronous Callback Sample 1:', json)
20 | return json
21 | })
22 | .catch((error) => {
23 | console.error(error)
24 | return null
25 | })
26 | })
27 | .catch((error) => {
28 | console.error(error)
29 | return null
30 | })
31 | }
32 |
33 | const profile = fetchProfileCallback()
34 | // 非同期処理が完了していないのでPromiseが表示される
35 | console.log('Asynchronous Callback Sample 2:', profile)
36 | }
37 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | module.exports = {
3 | // モジュールバンドルを行う起点となるファイルの指定
4 | // 指定できる値としては、ファイル名の文字列や、それを並べた配列やオブジェクト
5 | // 下記はオブジェクトとして指定した例
6 | entry: {
7 | bundle: './src/index.ts'
8 | },
9 | // モジュールバンドルを行った結果を出力する場所やファイル名の指定
10 | output: {
11 | path: path.join(__dirname, 'dist'), // "__dirname"はファイルが存在するディレクトリ
12 | filename: '[name].js' // [name]はentryで記述した名前(この設定ならbundle)
13 | },
14 | // import文でファイル拡張子を書かずに名前解決するための設定
15 | // 例...「import World from './world'」と記述すると"world.ts"という名前のファイルをモジュールとして探す
16 | resolve: {
17 | extensions:['.ts','.js'] // Reactの.tsxや.jsxの拡張子も扱いたい場合は配列内に追加する
18 | },
19 | devServer: {
20 | contentBase: path.join(__dirname, 'dist'), // webpack-dev-serverの公開フォルダ
21 | open: true // サーバー起動時にブラウザを開く
22 | },
23 | // モジュールに適用するルールの設定(ローダーの設定を行う事が多い)
24 | module: {
25 | rules: [
26 | {
27 | // 拡張子が.tsのファイルに対してTypeScriptコンパイラを適用する
28 | // Reactで用いる.tsxの拡張子にも適用する場合は test:/\.(ts|tsx)$/,
29 | test: /\.ts$/,
30 | loader: 'ts-loader'
31 | }
32 | ]
33 | }
34 | }
--------------------------------------------------------------------------------
/src/function/basic.ts:
--------------------------------------------------------------------------------
1 | // パラメーターがstring、戻り値がvoidのshowMessage関数
2 | // アロー関数
3 | export const logMessage = (message: string): void => {
4 | console.log('Function basic sample 1:', message)
5 | }
6 |
7 | // 名前付き関数
8 | export function logMessage2(message: string): void {
9 | console.log('Function basic sample 2:', message)
10 | }
11 |
12 | // 関数式
13 | export const logMessage3 = function (message: string): void {
14 | console.log('Function basic sample 3:', message)
15 | }
16 |
17 | // アロー関数式の省略記法
18 | export const logMessage4 = (message: string): void => console.log('Function basic sample 4:', message)
19 |
20 | export const alwaysThrowError = (message: string): never => {
21 | throw new Error(message)
22 | }
23 |
24 | // 呼び出しシグネチャ(省略記法)
25 | type LogMessage = (message: string) => void
26 | export const logMessage5: LogMessage = (message) => {
27 | console.log('Function basic sample 5:', message)
28 | }
29 |
30 | // 完全な呼び出しシグネチャ
31 | type FullLogMessage = {
32 | (message: string): void
33 | }
34 | export const logMessage6: FullLogMessage = (message) => {
35 | console.log('Function basic sample 6:', message)
36 | }
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ts-basic-demo",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "webpack --mode=production",
8 | "start": "webpack-cli serve --mode development",
9 | "lint": "eslint --fix 'src/**/*.{js,ts}'",
10 | "lint-fix": "eslint --fix './src/**/*.{js,ts}' && prettier --write './src/**/*.{js,ts}'"
11 | },
12 | "author": "Torahack",
13 | "license": "MIT",
14 | "devDependencies": {
15 | "@typescript-eslint/eslint-plugin": "^4.11.0",
16 | "@typescript-eslint/parser": "^4.11.0",
17 | "eslint": "^7.16.0",
18 | "eslint-config-prettier": "^7.1.0",
19 | "husky": "^4.3.6",
20 | "lint-staged": "^10.5.3",
21 | "prettier": "^2.2.1",
22 | "ts-loader": "^8.0.12",
23 | "typescript": "^4.1.3",
24 | "webpack": "^5.11.0",
25 | "webpack-cli": "^4.3.0",
26 | "webpack-dev-server": "^3.11.0"
27 | },
28 | "husky": {
29 | "hooks": {
30 | "pre-commit": "lint-staged"
31 | }
32 | },
33 | "lint-staged": {
34 | "src/**/*.{js,jsx,ts,tsx}": [
35 | "npm run lint-fix"
36 | ]
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/asynchronous/promise.ts:
--------------------------------------------------------------------------------
1 | export default function promiseSample() {
2 | const url = 'https://api.github.com/users/deatiger'
3 |
4 | type Profile = {
5 | login: string
6 | id: number
7 | }
8 |
9 | type FetchProfile = () => Promise
10 |
11 | const fetchProfilePromise: FetchProfile = () => {
12 | return new Promise((resolve, reject) => {
13 | fetch(url)
14 | .then((res) => {
15 | // レスポンスbodyをJSONとして読み取った結果を返す
16 | res
17 | .json()
18 | .then((json: Profile) => {
19 | console.log('Asynchronous Promise Sample 1:', json)
20 | resolve(json)
21 | })
22 | .catch((error) => {
23 | console.error(error)
24 | reject(null)
25 | })
26 | })
27 | .catch((error) => {
28 | console.error(error)
29 | reject(null)
30 | })
31 | })
32 | }
33 |
34 | fetchProfilePromise().then((profile: Profile | null) => {
35 | if (profile) {
36 | console.log('Asynchronous Promise Sample 2:', profile)
37 | }
38 | })
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 torahack
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/asynchronous/asyncAwait.ts:
--------------------------------------------------------------------------------
1 | export default async function asyncAwaitSample() {
2 | const url = 'https://api.github.com/users/deatiger'
3 |
4 | type Profile = {
5 | login: string
6 | id: number
7 | }
8 |
9 | type FetchProfile = () => Promise
10 |
11 | // async/awaitでコールバック関数を同期的な処理に置き換える
12 | const fetchProfile: FetchProfile = async () => {
13 | const response = await fetch(url)
14 | .then((response) => response)
15 | .catch((error) => {
16 | console.error(error)
17 | return null
18 | })
19 |
20 | // responseがnullならfetchに失敗している
21 | if (!response) {
22 | return null
23 | }
24 |
25 | const json = await response
26 | .json()
27 | .then((json: Profile) => {
28 | console.log('Asynchronous Promise Sample 1:', json)
29 | return json
30 | })
31 | .catch((error) => {
32 | console.error(error)
33 | return null
34 | })
35 |
36 | // jsonがnullならレスポンスBodyの読み取りに失敗している
37 | if (!json) {
38 | return null
39 | }
40 |
41 | return json
42 | }
43 |
44 | fetchProfile().then((profile: Profile | null) => {
45 | if (profile) {
46 | console.log('Asynchronous Promise Sample 2:', profile)
47 | }
48 | })
49 |
50 | // さらに同期的な処理にする
51 | const profile = await fetchProfile()
52 | if (profile) {
53 | console.log('Asynchronous Promise Sample 3:', profile)
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/object/object.ts:
--------------------------------------------------------------------------------
1 | export default function objectSample() {
2 | // const a: object = {
3 | // name: 'Torahack',
4 | // age: 28
5 | // }
6 | // a.name // aというobjectにはnameというプロパティがないとエラーが出る
7 | //
8 | // console.log("Object object sample 1:", a)
9 |
10 | // オブジェクトリテラル記法による型定義
11 | let country: {
12 | language: string
13 | name: string
14 | } = {
15 | language: 'Japanese',
16 | name: 'Japan',
17 | }
18 |
19 | console.log('Object object sample 2:', country)
20 |
21 | // 同じ構造のオブジェクトであれば再代入できる
22 | country = {
23 | language: 'English',
24 | name: 'United States of America',
25 | }
26 |
27 | console.log('Object object sample 3:', country)
28 |
29 | // オプショナル(?)なプロパティと読み取り専用(readonly)プロパティ
30 | const torahack: {
31 | age: number
32 | lastName: string
33 | readonly firstName: string
34 | gender?: string
35 | } = {
36 | age: 28,
37 | lastName: 'Yamada',
38 | firstName: 'Tarou',
39 | }
40 | torahack.lastName = 'Kamado'
41 | // torahack.firstName = 'Tanjiro' // firstNameプロパティはreadonlyなので再代入不可
42 | torahack.gender = 'male' // genderプロパティはオプショナルなので後から追加できる
43 |
44 | console.log('Object object sample 4:', torahack)
45 |
46 | // インデックスシグネチャ
47 | const capitals: {
48 | [countryName: string]: string
49 | } = {
50 | Japan: 'Tokyo',
51 | Korea: 'Seoul',
52 | }
53 | capitals.China = 'Beijing'
54 | capitals.Canada = 'Ottawa'
55 |
56 | console.log('Object object sample 5:', capitals)
57 | }
58 |
--------------------------------------------------------------------------------
/src/array/array.ts:
--------------------------------------------------------------------------------
1 | export default function arraySample() {
2 | // シンプルな配列の型定義
3 | const colors: string[] = ['red', 'blue']
4 | colors.push('yellow') // OK
5 | // colors.push(123) // NG
6 | console.log('Array array sample 1:', colors)
7 |
8 | const even: Array = [2, 4, 6]
9 | even.push(8) // OK
10 | // even.push("10") // NG
11 | console.log('Array array sample 2:', even)
12 |
13 | // 合併型を用いた配列の型定義
14 | const ids: (string | number)[] = ['ABC', 123]
15 | ids.push('DEF') // OK
16 | ids.push(456) // OK
17 | console.log('Array array sample 3:', ids)
18 |
19 | // 型推論された配列の生成
20 | const generateSomeArray = () => {
21 | const _someArray = [] // any[]
22 | _someArray.push(123) // number[]として推論される
23 | _someArray.push('ABC') // (string | number)[]として推論される
24 | return _someArray
25 | }
26 |
27 | const someArray = generateSomeArray()
28 | // someArray.push(true) // generateSomeArray()は(string | number)[]の配列を返すと推論されるので、booleanは追加されない
29 | console.log('Array array sample 4:', someArray)
30 |
31 | // 読み取り専用の配列
32 | const commands: readonly string[] = ['git add', 'git commit', 'git push']
33 | // commands.push("git fetch") // 追加不可
34 | // commands[2] = "git pull" // 代入不可
35 | console.log('Array array sample 5:', commands)
36 |
37 | // 読み取り専用の定義方法
38 | const immutableNumbers: ReadonlyArray = [1, 2, 3]
39 | // immutableNumbers.push(4) // NG
40 | console.log('Array array sample 6:', immutableNumbers)
41 |
42 | const immutableNames: Readonly = ['Tarou', 'Kazu', 'Yuto']
43 | // immutableNames.push("Takashi") // NG
44 | console.log('Array array sample 7:', immutableNames)
45 | }
46 |
--------------------------------------------------------------------------------
/src/interface/interface.ts:
--------------------------------------------------------------------------------
1 | // 宣言のマージ
2 | // インターフェースの場合
3 | interface Bread {
4 | calories: number
5 | }
6 |
7 | interface Bread {
8 | type: string
9 | }
10 |
11 | const francePan: Bread = {
12 | calories: 350,
13 | type: 'hard',
14 | }
15 |
16 | // 型エイリアスの場合
17 | type MaboDofu = {
18 | calories: number
19 | spicyLevel: number
20 | }
21 |
22 | type Rice = {
23 | calories: number
24 | gram: number
25 | }
26 |
27 | type MaboDon = MaboDofu & Rice // 交差型(union)を使う
28 |
29 | // MaboDofuとRice両方の型を持つ変数を定義
30 | const maboDon: MaboDon = {
31 | calories: 500,
32 | gram: 100,
33 | spicyLevel: 5,
34 | }
35 |
36 | // インターフェースの拡張
37 | interface Book {
38 | page: number
39 | title: string
40 | }
41 |
42 | interface Magazine extends Book {
43 | cycle: 'daily' | 'weekly' | 'monthly' | 'yearly'
44 | }
45 |
46 | const jump: Magazine = {
47 | cycle: 'weekly',
48 | page: 300,
49 | title: '週間少年ジャンプ',
50 | }
51 |
52 | // インターフェースは型エイリアスも拡張できる
53 | type BookType = {
54 | page: number
55 | title: string
56 | }
57 |
58 | interface HandBook extends BookType {
59 | theme: string
60 | }
61 |
62 | const cotrip: HandBook = {
63 | page: 120,
64 | theme: '旅行',
65 | title: 'ことりっぷ',
66 | }
67 |
68 | // インターフェースでクラスに型を定義する
69 | class Comic implements Book {
70 | page: number
71 | title: string
72 |
73 | constructor(page: number, title: string, private publishYear: string) {
74 | this.page = page
75 | this.title = title
76 | }
77 |
78 | getPublishYear() {
79 | return this.title + 'が発売されたのは' + this.publishYear + '年です。'
80 | }
81 | }
82 |
83 | const popularComic = new Comic(200, '鬼滅の刃', '2016')
84 | console.log(popularComic.getPublishYear()) // 鬼滅の刃が発売されたのは2016年です。
85 |
--------------------------------------------------------------------------------
/src/object/alias.ts:
--------------------------------------------------------------------------------
1 | export default function typeAliasSample() {
2 | // 型エイリアス(type)
3 | type Country = {
4 | capital: string
5 | language: string
6 | name: string
7 | }
8 |
9 | const japan: Country = {
10 | capital: 'Tokyo',
11 | language: 'Japanese',
12 | name: 'Japan',
13 | }
14 | console.log('Object alias sample 1:', japan)
15 |
16 | const america: Country = {
17 | capital: 'Washington, D.C.',
18 | language: 'English',
19 | name: 'United States of America',
20 | }
21 | console.log('Object alias sample 2:', america)
22 |
23 | // 合併型(union)と交差型(intersection)
24 | type Knight = {
25 | hp: number
26 | sp: number
27 | weapon: string
28 | swordSkill: string
29 | }
30 |
31 | type Wizard = {
32 | hp: number
33 | mp: number
34 | weapon: string
35 | magicSkill: string
36 | }
37 |
38 | type Adventurer = Knight | Wizard // 合併型: KnightとWizardどちらかの型を持つ
39 | type Paladin = Knight & Wizard // 交差型: KnightとWizardが持つ型を全て持っている
40 |
41 | // Knightの型を持つadventurer2
42 | const adventurer1: Adventurer = {
43 | hp: 100,
44 | sp: 30,
45 | weapon: '木の剣',
46 | swordSkill: '三連斬り',
47 | }
48 |
49 | console.log('Object alias sample 3:', adventurer1)
50 |
51 | // Wizardの型を持つadventurer2
52 | const adventurer2: Adventurer = {
53 | hp: 100,
54 | mp: 30,
55 | weapon: '木の杖',
56 | magicSkill: 'ファイヤボール',
57 | }
58 |
59 | console.log('Object alias sample 4:', adventurer2)
60 |
61 | // KnightとWizard両方の型を持つpaladin
62 | const paladin: Paladin = {
63 | hp: 300,
64 | sp: 100,
65 | mp: 100,
66 | weapon: '銀の剣',
67 | swordSkill: '三連斬り',
68 | magicSkill: 'ファイヤボール',
69 | }
70 |
71 | console.log('Object alias sample 5:', paladin)
72 | }
73 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | // 「01.環境構築」と「02.ESLint&Prettier」のサンプルコード
2 | // import World from './world'
3 | // const root: HTMLElement | null = document.getElementById('root')
4 | // const world = new World('Hello World!!!')
5 | // world.sayHello(root)
6 |
7 | // 「03.基本の型定義」のサンプルコード
8 | // import { anySample, notExist, primitiveSample, unknownSample } from './basic'
9 | // anySample()
10 | // notExist()
11 | // primitiveSample()
12 | // unknownSample()
13 |
14 | // 「04.関数の型定義」のサンプルコード
15 | // import { logMessage, logMessage5 } from './function/basic'
16 | // import { isUserSignedIn, isUserSignedIn2, sumProductsPrice } from './function/parameters'
17 | //
18 | // logMessage('Hello TypeScript!')
19 | // logMessage5('Hello Type Signature!')
20 | // isUserSignedIn('ABC', 'Torahack')
21 | // isUserSignedIn('123')
22 | // isUserSignedIn2('ABC')
23 | // const sum = sumProductsPrice(1000, 2000, 500, 3000, 250)
24 | // console.log('Function parameters sample 5: Total price:', sum)
25 |
26 | // 「05.オブジェクトの型定義」のサンプルコード
27 | // import objectSample from './object/object'
28 | // import typeAliasSample from './object/alias'
29 | // objectSample()
30 | // typeAliasSample()
31 |
32 | // 「06.配列とタプルの型定義」のサンプルコード
33 | // import arraySample from './array/array'
34 | // import tupleSample from './array/tuple'
35 | // arraySample()
36 | // tupleSample()
37 |
38 | // 「07.ジェネリック型とポリモーフィズム」のサンプルコード
39 | // import genericsSample from './generics/basic'
40 | // import advancedSample from './generics/advanced'
41 | // genericsSample()
42 | // advancedSample()
43 |
44 | // 「10.非同期処理の型定義」のサンプルコード
45 | import callbackSample from './asynchronous/callback'
46 | import promiseSample from './asynchronous/promise'
47 | import asyncAwaitSample from './asynchronous/asyncAwait'
48 | // callbackSample()
49 | // promiseSample()
50 | asyncAwaitSample()
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # License
2 | The source code is licensed MIT.
3 |
4 | ## 1.環境構築
5 |
6 | ### 1-1. Node環境の確認とインストール
7 | 1. Homebrewのバージョンを確認
8 | `brew --version`
9 | 2. インストールされていなければ実行
10 | `/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"`
11 | 3. nodebrewのインストール
12 | `brew install nodebrew`
13 | 4. nodeの安定バージョンをインストール
14 | `nodebrew install stable`
15 | 5. nodebrewのバージョン一覧を確認
16 | `nodebrew ls`
17 | 6. インストールしたバージョンを指定して切り替える
18 | `nodebrew use v14.15.3`
19 | 7. nodeのパスを通す
20 | `echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.zprofile'`
21 | 8. ターミナルの再起動
22 | 9. nodeとnpmのバージョン確認
23 | `node -v`
24 | `npm -v`
25 |
26 | ### 1-2. TypeScript環境構築
27 | 1. 開発用ディレクトリの作成
28 | `mkdir ~//ts-basic`
29 | 2. npmの初期化
30 | `npm init`
31 | 3. 関連パッケージのインストール(※)
32 | `npm install --save-dev typescript ts-loader webpack webpack-cli webpack-dev-server`
33 | 4. webpack.config.jsの作成と設定
34 | 5. tsconfig.jsonの作成と設定
35 | `tsc --init`
36 |
37 | #### ※ 実行時に`gyp: No Xcode or CLT version detected!`のエラーが出たら
38 |
39 | Mac OSに入っているXCodeのバージョンと、コマンドラインツールのバージョンが合っていないという理由で怒られています。
40 | 以下手順でコマンドラインツールのバージョンをアップデートしましょう。
41 |
42 | sudo付きのコマンドはMac OSのパスワード入力が必要です。
43 | 4.を実行すると、コマンドラインツールをインストールするか聞かれるので、同意して進めてください。
44 |
45 | 1. `sudo rm -rf $(xcode-select -print-path)`
46 | 2. `sudo rm -rf /Library/Developer/CommandLineTools`
47 | 3. `sudo xcode-select --reset`
48 | 4. `xcode-select --install`
49 | 5. `xcode-select -p`
50 | 6. 5.の実行結果が`/Library/Developer/CommandLineTools`でなければ`sudo xcode-select -switch /Library/Developer/CommandLineTools`を実行
51 |
52 | インストールが完了したら、「3.関連パッケージのインストール」のコマンドを再実行してください。
53 |
54 | ## 2. ESLintとPrettierのCI環境を構築
55 | ### 2-1. パッケージのインストール
56 | `npm install --save-dev eslint eslint-config-prettier prettier @typescript-eslint/parser @typescript-eslint/eslint-plugin husky lint-staged`
57 |
58 | ### 2-2. huskyがもし動かなかったら...
59 | .git/hooksが正常に作成されていない可能性アリ
60 | これで確認する
61 | `ls -la .git/hooks/ls -la .git/hooks/`
62 |
63 | `.sample`しかなかったらNG
64 | NGの場合はインストールし直す
65 | `npm uninstall huksy`
66 | `npm install --save-dev husky`
67 | もう一度hooksを確認
68 | `ls -la .git/hooks/ls -la .git/hooks/`
69 |
--------------------------------------------------------------------------------
/src/generics/basic.ts:
--------------------------------------------------------------------------------
1 | export default function genericsSample() {
2 | // ジェネリック型を使わない場合
3 | const stringReduce = (array: string[], initialValue: string): string => {
4 | let result = initialValue
5 | for (let i = 0; i < array.length; i++) {
6 | const item = array[i]
7 | result += item
8 | }
9 | return result
10 | }
11 | console.log('Generics basic sample 1:', stringReduce(['May ', 'the ', 'force ', 'be ', 'with ', 'you'], ''))
12 |
13 | const numberReduce = (array: number[], initialValue: number): number => {
14 | let result = initialValue
15 | for (let i = 0; i < array.length; i++) {
16 | const item = array[i]
17 | result += item
18 | }
19 | return result
20 | }
21 | console.log('Generics basic sample 2:', numberReduce([100, 200, 300], 1000))
22 |
23 | // 型エイリアスを作るとしたら...?
24 | type Reduce = {
25 | (array: string[], initialValue: string): string
26 | (array: number[], initialValue: number): number
27 | }
28 |
29 | // ジェネリック型でまとめる
30 | type GenericReduce = {
31 | (array: T[], initialValue: T): T
32 | }
33 |
34 | // ジェネリック型を使った呼び出し
35 | const genericStringReduce: GenericReduce = (array, initialValue) => {
36 | let result = initialValue
37 | for (let i = 0; i < array.length; i++) {
38 | const item = array[i]
39 | result += item
40 | }
41 | return result
42 | }
43 | console.log('Generics basic sample 3:', genericStringReduce(['MAKE ', 'TYPESCRIPT ', 'GREAT ', 'AGAIN'], ''))
44 |
45 | const genericNumberReduce: GenericReduce = (array, initialValue) => {
46 | let result = initialValue
47 | for (let i = 0; i < array.length; i++) {
48 | const item = array[i]
49 | result += item
50 | }
51 | return result
52 | }
53 | console.log('Generics basic sample 4:', numberReduce([-100, -200, -300], 1000))
54 |
55 | // いろいろなジェネリック型の定義方法
56 | // 完全な呼び出しシグネチャ(個々のシグネチャにジェネリック型を割り当てる)
57 | type GenericReduce2 = {
58 | (array: T[], initialValue: T): T
59 | (array: U[], initialValue: U): U
60 | }
61 | // 呼び出しシグネチャの省略記法
62 | type GenericReduce3 = (array: T[], initialValue: T) => T
63 | type GenericReduce4 = (array: T[], initialValue: T) => T
64 | }
65 |
--------------------------------------------------------------------------------
/src/oop/shogi.ts:
--------------------------------------------------------------------------------
1 | type Player = 'first' | 'second'
2 | type Suji = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
3 | type Dan = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
4 |
5 | class Position {
6 | // private修飾子のついたプロパティは、Positionクラスから生まれたインスタンスのみで使用できる
7 | constructor(private suji: Suji, private dan: Dan) {}
8 |
9 | // パラメーターに渡された位置と現在の位置を比較するメソッド
10 | distanceFrom(position: Position, player: Player) {
11 | if (player === 'first') {
12 | return {
13 | suji: Math.abs(position.suji - this.suji),
14 | dan: Math.abs(Number(position.dan) - Number(this.dan)),
15 | }
16 | } else {
17 | return {
18 | suji: Math.abs(position.suji - this.suji),
19 | dan: -Math.abs(Number(position.dan) - Number(this.dan)), // 段(縦の距離)は正負反転
20 | }
21 | }
22 | }
23 | }
24 |
25 | // Pieceは抽象クラス
26 | abstract class Piece {
27 | // protected修飾子のついたプロパティは、Pieceクラスとサブクラスから生まれたインスタンスで使用できる
28 | protected position: Position
29 |
30 | constructor(private readonly player: Player, suji: Suji, dan: Dan) {
31 | this.position = new Position(suji, dan)
32 | }
33 |
34 | // パラメーターに渡された位置へ駒を移動するメソッド
35 | // publicなので、サブクラスでオーバーライド(上書き)できる
36 | moveTo(position: Position) {
37 | this.position = position
38 | }
39 |
40 | // 移動できるかどうか判定するメソッド
41 | // abstractをつけて宣言しておき、サブクラスで具体的に実装する
42 | abstract canMoveTo(position: Position, player: Player): boolean
43 | }
44 |
45 | // Pieceクラスを継承したOshoクラスを宣言
46 | class Osho extends Piece {
47 | canMoveTo(position: Position, player: Player): boolean {
48 | // 移動先に指定された位置(position)と現在の位置(this.position)を比較
49 | const distance = this.position.distanceFrom(position, player)
50 | // 移動先との距離が2未満、つまり1マス以内なら移動できる
51 | return distance.suji < 2 && distance.dan < 2
52 | }
53 | }
54 |
55 | class Fu extends Piece {
56 | canMoveTo(position: Position, player: Player): boolean {
57 | const distance = this.position.distanceFrom(position, player)
58 | // 移動先との距離が前方1マスであれば
59 | return distance.suji === 0 && distance.dan === 1
60 | }
61 | }
62 |
63 | class NariKin extends Fu {
64 | canMoveTo(position: Position, player: Player): boolean {
65 | const distance = this.position.distanceFrom(position, player)
66 | return (
67 | distance.suji < 2 &&
68 | distance.dan < 2 && // 移動先が1マス以内
69 | distance.suji !== 0 &&
70 | distance.dan === -1 // 左後方と右後方には進めない
71 | )
72 | }
73 | }
74 |
75 | class Game {
76 | private pieces = Game.makePieces()
77 | private static makePieces() {
78 | return [
79 | new Osho('first', 5, '1'),
80 | new Osho('second', 5, '9'),
81 |
82 | // 先手の歩
83 | new Fu('first', 1, '3'),
84 | new Fu('first', 2, '3'),
85 | new Fu('first', 3, '3'),
86 | new Fu('first', 4, '3'),
87 | new Fu('first', 5, '3'),
88 | new Fu('first', 6, '3'),
89 | new Fu('first', 7, '3'),
90 | new Fu('first', 8, '3'),
91 | new Fu('first', 9, '3'),
92 |
93 | // 後手の歩
94 | new Fu('second', 1, '7'),
95 | new Fu('second', 2, '7'),
96 | new Fu('second', 3, '7'),
97 | new Fu('second', 4, '7'),
98 | new Fu('second', 5, '7'),
99 | new Fu('second', 6, '7'),
100 | new Fu('second', 7, '7'),
101 | new Fu('second', 8, '7'),
102 | new Fu('second', 9, '7'),
103 | ]
104 | }
105 | }
106 |
107 | new Game()
108 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
8 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./dist", /* Redirect output structure to the directory. */
18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | // "strictNullChecks": true, /* Enable strict null checks. */
31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | // "noUnusedLocals": true, /* Report errors on unused locals. */
39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
43 |
44 | /* Module Resolution Options */
45 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
46 | "baseUrl": "./src", /* Base directory to resolve non-absolute module names. */
47 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
48 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
49 | // "typeRoots": [], /* List of folders to include type definitions from. */
50 | // "types": [], /* Type declaration files to be included in compilation. */
51 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
52 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
53 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
54 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
55 |
56 | /* Source Map Options */
57 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
58 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
59 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
60 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
61 |
62 | /* Experimental Options */
63 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
64 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
65 |
66 | /* Advanced Options */
67 | "skipLibCheck": true, /* Skip type checking of declaration files. */
68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
69 | }
70 | }
71 |
--------------------------------------------------------------------------------