Created
May 26, 2018 09:08
-
-
Save thesephist/e3b35bea6a3318407ee28a92dc43d858 to your computer and use it in GitHub Desktop.
TypeScript Todo Example (incl. baseline webpack + tsconfig)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
<title>TypeScript Practice</title> | |
<meta name="viewport" content="width=device-width, initial-scale=1"> | |
<style> | |
body { | |
font-family: sans-serif; | |
max-width: 800px; | |
margin: 20px auto; | |
} | |
</style> | |
</head> | |
<body> | |
<main></main> | |
<script src="/js/main.min.js"></script> | |
</body> | |
< |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let __taskid_ref = 0; | |
const taskid = (): number => { | |
return __taskid_ref ++; | |
} | |
interface Component { | |
html(): string; | |
} | |
interface ClickEvent extends Event { | |
readonly target: HTMLElement; | |
} | |
interface KeyboardEvent extends Event { | |
readonly target: HTMLElement; | |
} | |
class TaskComponent implements Component { | |
readonly id: number = taskid(); | |
private name: string; | |
constructor({ | |
name = 'Do something...', | |
} = {}) { | |
this.name = name; | |
} | |
html(): string { | |
return `<li class="task" data-id="${this.id}"> | |
${this.name} | |
<button class="removeButton">X</button> | |
</li>`; | |
} | |
setName(name: string): void { | |
this.name = name; | |
} | |
} | |
class ListComponent implements Component { | |
tasks: TaskComponent[]; | |
constructor({ | |
tasks = [], | |
}: { | |
tasks?: TaskComponent[], | |
} = {}) { | |
this.tasks = []; | |
} | |
addTask(value: string): void { | |
this.tasks.push(new TaskComponent({name: value})); | |
} | |
removeTask(id: number) { | |
this.tasks = this.tasks.filter(t => t.id !== id); | |
} | |
html(): string { | |
return `<ul class="list"> | |
${this.tasks.map(t => t.html()).join(' ')} | |
</ul>`; | |
} | |
} | |
class TodoApp implements Component { | |
readonly list: ListComponent = new ListComponent(); | |
addTask(value: string): void { | |
this.list.addTask(value); | |
render(); | |
} | |
removeTask(id: number): void { | |
this.list.removeTask(id); | |
render(); | |
} | |
html(): string { | |
return ` | |
<h1>To-do List</h1> | |
<input type="text" id="todoInput"> | |
<button id="addButton">Add</button> | |
<div class="todolist"> | |
${this.list.html()} | |
</div> | |
`; | |
} | |
} | |
// Main calls | |
const $main: HTMLElement = document.querySelector('main'); | |
let $input: HTMLInputElement; | |
let $addButton: HTMLButtonElement; | |
const app = new TodoApp(); | |
const render = (): void => { | |
$main.innerHTML = app.html(); | |
$input = document.querySelector('#todoInput'); | |
$addButton = document.querySelector('#addButton'); | |
} | |
render(); | |
// Other App Elements. It's kinda dirty b/c my component model is fucked up. | |
// Add listeners | |
$main.addEventListener('click', (evt: ClickEvent): void => { | |
if (evt.target.classList.contains('removeButton')) { | |
const taskid: number = +evt.target.parentElement.getAttribute('data-id'); | |
app.removeTask(taskid); | |
} else if (evt.target.id === 'addButton') { | |
if ($input.value.trim()) { | |
app.addTask($input.value.trim()); | |
$input.value = ''; | |
$input.focus(); | |
} | |
} | |
}); | |
$main.addEventListener('keypress', (evt: KeyboardEvent): void => { | |
if (evt.target.id == 'todoInput') { | |
if (evt.keyCode === 13 && $input.value.trim()) { | |
app.addTask($input.value.trim()); | |
$input.value = ''; | |
$input.focus(); | |
} | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "ts-practice", | |
"version": "1.0.0", | |
"description": "Intro to TypeScript", | |
"main": "index.js", | |
"author": "Linus Lee", | |
"license": "MIT", | |
"scripts": { | |
"build": "webpack" | |
}, | |
"devDependencies": { | |
"webpack-cli": "^2.1.4", | |
"webpack": "^4.9.1", | |
"ts-loader": "^4.3.0", | |
"typescript": "^2.8.3" | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{ | |
"compilerOptions": { | |
"outDir": "./static/js/", | |
"sourceMap": true, | |
"noImplicitAny": true, | |
"module": "ESNext", | |
"target": "ESNext", | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require('path'); | |
module.exports = { | |
mode: 'development', | |
entry: './src/main.ts', | |
devtool: 'inline-source-map', | |
watch : true, | |
watchOptions: { | |
ignored: /node_modules/, | |
}, | |
module: { | |
rules: [ | |
{ | |
test: /\.ts$/, | |
use: 'ts-loader', | |
exclude: '/node_modules/' | |
} | |
] | |
}, | |
resolve: { | |
extensions: ['.ts', '.js'] | |
}, | |
output: { | |
filename: 'main.min.js', | |
path: path.resolve(__dirname, 'static/js') | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment