├── .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 | --------------------------------------------------------------------------------