TypeScript Project : Sort

 mkdir sort

cd sort

mkdir src

mkdir build

code .

tsc --init

rootDir的路徑設定為    "rootDir": "./src",  

把outDir路徑設為"outDir": "./build",  

這樣ts檔跟編譯後的js檔產生路徑就設定好了

之後也可以嘗試tsc -w

可以直接編譯所有設定在rootDir的ts檔

但這樣每次都要下指令太麻煩了

所以可以在sort資料夾路徑用cmd下指令: npm init

先產生一個package.json檔

之後輸入指令npm install nodemon concurrently

加入nodemon 跟concurrently

然後修改裡面的script內容

  "scripts": {
    "start:build": "tsc -w",
    "start:run": "nodemon build index.js",
    "start": "concurrently npm:start:*"
  },

這表示每次都會同時啟動所有start:開頭的script

以上都設定好後,我們在src內新增一個index.ts檔

console.log("HELLO WORLD");
console.log("TEST");

然後在cmd輸入npm start






就可以看到他自動啟動我們剛剛設定在script內的指令了


接下來是project介紹

這個sort project是要實作演算法裡面的bubble sort

可以參考這個網址

https://pjchender.blogspot.com/2017/09/bubble-sort.html


在src下建立Sorter.ts

export abstract class Sorter {
  abstract compare(leftIndex: number, rightIndex: number): boolean;
  abstract swap(leftIndex: number, rightIndex: number): void;
  abstract length: number;

  sort(): void {
    const { length } = this;

    for (let i = 0; i < length; i++) {
      for (let j = 0; j < length - i - 1; j++) {
        if (this.compare(j, j + 1)) {
          this.swap(j, j + 1);
        }
      }
    }
  }
}

在src下建立NumberCollection.ts

import { Sorter } from './Sorter';

export class NumbersCollection extends Sorter {
  constructor(public data: number[]) {
    super();
  }

  get length(): number {
    return this.data.length;
  }

  compare(leftIndex: number, rightIndex: number): boolean {
    return this.data[leftIndex] > this.data[rightIndex];
  }

  swap(leftIndex: number, rightIndex: number): void {
    const leftHand = this.data[leftIndex];
    this.data[leftIndex] = this.data[rightIndex];
    this.data[rightIndex] = leftHand;
  }
}

把index.ts內容修改如下


import { NumbersCollection } from './NumbersCollection';
const numbersCollection = new NumbersCollection([50, 3, -5, 0]);
numbersCollection.sort();
console.log(numbersCollection.data);

之後執行npm start

就能看到bubble sort的結果了




Types in TypeScript

 TypeScript有Primitive Types 跟 Object Type


Primitive Types : number, boolean, string, null, void, undefined, symbol

Object Types: functions, classes, array, object


First App in TypeScript

打開cmd

輸入 mkdir fetchJson (意思是創建一個叫fetchJson的資料夾)

然後輸入 cd fetchJson(進入這個資料夾)

輸入  npm init -y  (產生package.json檔)

輸入  npm install axios (安裝axios套件)

最後輸入 code . 就可以直接開啟載入這個資料夾的vscode

在資料夾內新增一個index.ts

然後輸入以下code

import axios from "axios";

const url = "https://jsonplaceholder.typicode.com/todos/1"

axios.get(url).then(response => {
  console.log(response.data)
})

之後在cmd輸入 tsc index.ts 去編譯這個ts檔

結束後會看到資料夾內多了一個index.js  這就是剛剛的index.ts編譯出來的js檔

在cmd輸入 node index.js

就可以得到以下資料




或是輸入ts-node index.ts 也可以




之後將程式修改如下 再執行一次ts-node index.ts 

import axios from "axios";

const url = "https://jsonplaceholder.typicode.com/todos/1"

axios.get(url).then(response => {
  const todo = response.data;
  const ID = todo.id
  const title = todo.title
  const finished = todo.completed

  console.log(`
  ID = ${ID}
  title = ${title}
  finished = ${finished}
  `)
})



最後再修改程式碼如下
定義一個Todo介面

import axios from "axios";

const url = "https://jsonplaceholder.typicode.com/todos/1"

interface Todo{
  id:number;
  title:string;
  completed: boolean
}

axios.get(url).then(response => {
  const todo = response.data as Todo;
  const ID = todo.id
  const title = todo.title
  const finished = todo.completed

  console.log(`
  ID = ${ID}
  title = ${title}
  finished = ${finished}
  `)
})


把console.log功能拆出來 會發現下面有紅線 代表錯誤












原因是跟傳入的參數名稱不一致  修正就好了

這也是typescript的好處  在開發時就會檢查錯誤



Typescript簡介

What is typescript?
Typescript = Javascript + Type system


TypeScript vs JavaScript:

Advantage of TypeScript over JavaScript  

  • TypeScript always highlights errors at compilation time during the time of development, whereas JavaScript points out errors at the runtime.
  • TypeScript supports strongly typed or static typing, whereas this is not in JavaScript.
  • TypeScript runs on any browser or JavaScript engine.
  • Great tooling supports with IntelliSense, which provides active hints as the code is added.
  • It has a namespace concept by defining a module.

Disadvantage of TypeScript over JavaScript

  • TypeScript takes a long time to compile the code.
  • TypeScript does not support abstract classes.
  • If we run the TypeScript application in the browser, a compilation step is required to transform TypeScript into JavaScript.

React 事件處理

 

事件處理

使用 React element 處理事件跟使用 DOM element 處理事件是十分相似的。它們有一些語法上的差異:

  • 事件的名稱在 React 中都是 camelCase,而在 HTML DOM 中則是小寫。
  • 事件的值在 JSX 中是一個 function,而在 HTML DOM 中則是一個 string。

例如,在 HTML 中的語法:

<button onclick="activateLasers()">
  Activate Lasers
</button>

和在 React 中的語法有些微的不同:

<button onClick={activateLasers}>  Activate Lasers
</button>

另外一個差異是,在 React 中,你不能夠使用 return false 來避免瀏覽器預設行為。你必須明確地呼叫 preventDefault。例如,在純 HTML 中,若要避免連結開啟新頁的預設功能,你可以這樣寫:

<form onsubmit="console.log('You clicked submit.'); return false">
  <button type="submit">Submit</button>
</form>

在 React 中,你則可以這樣寫:

function Form() {
  function handleSubmit(e) {
    e.preventDefault();    console.log('You clicked submit.');
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit">Submit</button>
    </form>
  );
}

在這裡,e 是一個 synthetic 事件(synthetic event)。React 根據 W3C 規範來定義這些 synthetic 事件,React event 與 native event 工作的方式不盡然相同。若想了解更多這方面的資訊,請參考 SyntheticEvent

當使用 React 時,你不需要在建立一個 DOM element 後再使用 addEventListener 來加上 listener。你只需要在這個 element 剛開始被 render 時就提供一個 listener。

當你使用 ES6 class 來定義 Component 時,常見的慣例是把 event handler 當成那個 class 的方法。例如,這個 Toggle Component 會 render 一個按鈕,讓使用者可以轉換 state 中的「開」與「關」:

class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 為了讓 `this` 能在 callback 中被使用,這裡的綁定是必要的:    this.handleClick = this.handleClick.bind(this);  }

  handleClick() {    this.setState(prevState => ({      isToggleOn: !prevState.isToggleOn    }));  }
  render() {
    return (
      <button onClick={this.handleClick}>        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

ReactDOM.render(
  <Toggle />,
  document.getElementById('root')
);

請特別注意 this 在 JSX callback 中的意義。在 JavaScript 中,class 的方法在預設上是沒有被綁定(bound)的。如果你忘了綁定 this.handleClick 並把它傳遞給 onClick 的話,this 的值將會在該 function 被呼叫時變成 undefined

這並非是 React 才有的行為,而是 function 在 JavaScript 中的運作模式。總之,當你使用一個方法,卻沒有在後面加上 () 之時(例如當你使用 onClick={this.handleClick} 時),你應該要綁定這個方法。

如果呼叫 bind 對你來說很麻煩的話,你可以用別的方式。如果你使用了還在測試中的 class fields 語法的話,你可以用 class field 正確的綁定 callback:

class LoggingButton extends React.Component {
  // 這個語法確保 `this` 是在 handleClick 中被綁定:  // 警告:這是一個還在*測試中*的語法:  handleClick = () => {    console.log('this is:', this);  }
  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    );
  }
}

這個語法在 Create React App 中是預設成可行的。

如果你並沒有使用 class field 的語法的話,你則可以在 callback 中使用 arrow function

class LoggingButton extends React.Component {
  handleClick() {
    console.log('this is:', this);
  }

  render() {
    // 這個語法確保 `this` 是在 handleClick 中被綁定:    return (      <button onClick={() => this.handleClick()}>        Click me
      </button>
    );
  }
}

這個語法的問題是每一次 LoggingButton render 的時候,就會建立一個不同的 callback。大多時候,這是無所謂的。然而,如果這個 callback 被當作一個 prop 傳給下層的 component 的話,其他的 component 也許會做些多餘的 re-render。原則上來說,我們建議在 constructor 內綁定,或使用 class field 語法,以避免這類的性能問題。

將參數傳給 Event Handler

在一個迴圈中,我們常常會需要傳遞一個額外的參數給 event handler。例如,如果 id 是每一行的 ID 的話,下面兩種語法都可行:

<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

以上這兩行程式是相同的。一個使用 arrow functions,另一個則使用了Function.prototype.bind

以這兩個例子來說,e 這個參數所代表的 React 事件將會被當作 ID 之後的第二個參數被傳遞下去。在使用 arrow function 時,我們必須明確地將它傳遞下去,但若使用 bind 語法,未來任何的參數都將會自動被傳遞下去。