---
# System prepended metadata

title: Typescript

---

Typescript
===

==參考資料：==
---
https://willh.gitbook.io/typescript-tutorial/
https://www.typescriptlang.org/docs/handbook/intro.html




==原始資料型別：==
---
::: info
boolean (布林值)、number (數值)、string (字串)、null、undefined 、Symbol。 
:::



- ### Boolean
 ```javascript=
 let Simple: boolean  = false;
```

- ### Number
 ```javascript=
let Num: number = 6;

// 支援二進位制表示法
let Num: number = 0xf00d;

// 支援二進位制表示法
let binaryLiteral: number = 0b1010;
```

- ### String
```javascript=
let myName: string = 'Zoe';
let lunch: number = 100;

// 範本字串
let Simple: string = `Hello, my name is ${myName}.
I spend ${lunch} dollars for lunch`;
```

- ### void 空值 , 用來宣告一個沒有return值的函數
```javascript=
let GreetThree :(a :string, b: string) => void ;

GreetThree = (name: string, greeting : string) => {
    console.log(`${name} says ${greeting}`)
}
```

- ### Ｎull & Undefined
  - **vs void** : **undefined 和 null 是所有型別的子型別**。
  - undefined 和 null 可以賦值給其他型別的變數，但void卻不行。
```javascript=
let num: number = undefined;  //ok

let n :undefined;
let num: number = n;  //ok

let v : void;
let num: number = v;
// Error : Type 'void' is not assignable to type 'number'.
```
<br>
<br>

==Any、Array、Tuple、Enum==
---


- ### Any
```javascript=
//使用any賦值後，可任意更改成任何type
//宣告一個變數為任意值之後，對它的任何操作，返回的內容的型別都是任意值。
let anyAge : any = 30; //number
anyAge = true;  //boolen
console.log(anyAge) //true

anyAge = "hello"
console.log(anyAge) //'hello'

let mixedarray : any[] = []
mixedarray.push("30");
mixedarray.push(30);
mixedarray.push(false);
console.log(mixedarray) //['30',30,fasle]

//可呼叫任何方法，傳回的值不論任何型別都可以：
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');

//在一開始宣告變數未給定型別即視為any
let something;
something = 'seven';  //string -> ok
something = 7;     //重新賦值number -> ok
```

- ### Array
    - 三種宣告方式
      1.  dataTyp[]
      2.  Array<dataType>
      3. [dataType] 
    
```javascript=
const user: string[] = ["aa", "bb", "cc"];
    
const idNumber: Array<number> = [1, 2, 3];
    
const mix: [string,number] = ['hi', 3];
```
<br>
    
```javascript=
let number = [10,20,30]
number.push(30);
number.push("a"); // Error, 遵循第一次建立的type

let mix: Array<string | number | boolen> = [10, 'array' , true] 
let str : Array<string> = ['123']
mix.push(20)
mix.push('hello')  //both OK


//用泛型宣告陣列

let mixArray: Array<number> = [1, 1, 2, 3, 5];
```

- ### Tuple
```javascript=
//一旦宣告賦值需提供指定的項
let tom: [string, number];
tom = ['Tom']  //Error : required in type '[string, number]'.
tom = ['Tom', 25];   //OK
```


- ### Enum
```javascript=
//未給值狀況下可像陣列一樣的用0~最尾數去更改或讀值。
Ex1. 
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 0); // true
console.log(Days["Tue"] === 2); // true

console.log(Days[0] === "Sun"); // true
console.log(Days[6] === "Sat"); // true


Ex2.
//增加可讀性溝通

enum PodCast { 
    Success : 1,
    Fail : -1,
    Streaming : 0        
}

const status = PodCast.Succsss
console.log(status)
```
<br>
<br>

==union types 聯合型別==
---
```javascript=
let mixed : (string|number|boolean)[] = []
mixed.push("hello")   //ok
mixed.push(20)        //ok
mixed.push(true)      //ok


function getLength(something: string | number): number {
    return something.length;
}  
//Error : Property 'length' does not exist on type 'number'.
//傳進去的值可能為 string or number, length不為number的屬性
//加上判斷式
function getLength(something: string | number): number {
    if(typeof something === "string"){
        return something.length;
    }else{
        return something
    }
}


let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 報錯
```
<br>
<br
    
==型別推論(Type Inference)、型別註記(Type Annotation)和型別斷言(Type Assertion)==
---
- 型別推論 : 沒有明確註記資料型別，TS編譯器便會自動推論出資料型別。
    
![](https://i.imgur.com/SZlkjkZ.png)
    
![](https://i.imgur.com/nQylPlh.png)

- 型別註記 : 明確的宣告資料型別，告訴編譯器必須符合註記的型別，在開發時就抓到錯誤賦值。  
![](https://i.imgur.com/brrlqzn.png)

==斷言 As==
---
 - TS認為開發者更了解自己編寫的程式碼。因此允許開發者覆蓋它的推論。編譯器會接受開發者寫下的斷言，並且不會再送出警告錯誤。

 - 兩種用法
     - <型別>值
     - 值 as 型別
    
 - 不能強制斷言成原本不存在的型別，必須透過將型別斷言成unknown，再行斷言。 
![](https://i.imgur.com/aFBH1SR.png)


 - 和型別推論不同之處
    - ==Type annotation== makes sure that the ==exact type== match.
    - ==Type assertion== makes sure that the ==subtype== match.
ex. 
![](https://i.imgur.com/muKms3s.png)
    
    
### 適用1. 將變數指定為更加具體的型別(when you need to narrow           down the type)
    
```javascript=
Ex1.
function getLength(something: string | number): number {
    if ((something as string)) {
        return (something as string).length;
    } else {
        return something.toString().length;
    }
}
    
    
Ex2.
interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}

function isFish(animal: Cat | Fish) {
    if (typeof (animal as Fish).swim === 'function') {
        return true;
    }
    return false;
}
```
Ex3.
![](https://i.imgur.com/gwb4QTV.png)


### 適用2. 當函式回傳any type，用斷言將any轉換成一個精確的型態會是比較好的做法。
```javascript=
//old
function getCacheData(key: string): any {
    return (window as any).cache[key];
}
    
    
//new    
function getCacheData(key: string): any {
    return (window as any).cache[key];
}

interface Cat {
    name: string;
    run(): void;
}

const tom = getCacheData('tom') as Cat;
tom.run();

```
### 適用3. 使用第三方的資源（Third-party resources）、呼叫會回傳未知結果的函示等。
```javascript=
//宣告fetch回來的值的type
interface Data {
    name:string,
    age:number,
    uid:number 
}

async function getData(){
    const res = await fetch('http://jsonplaceholder.typicode.com/todos/1')
    const data = await res.json() as Data
}
```
### phoenix
![](https://i.imgur.com/oPlE4Py.png)

![](https://i.imgur.com/2vt1bNZ.png)

    
### As 壞處
1. 編譯器不會提醒你要加入的properties
```javascript=
//use 型別註記
interface Foo {
    bar: number;
    bas: string;
}
var foo: Foo = {
// the compiler will provide autocomplete for   
// properties of Foo
};
    
    
//use Assertion
interface Foo {
    bar: number;
    bas: string;
}
var foo = {} as Foo;
// ahhhh .... forget something?

```
    
2. 斷言取代type check，無法得知正確型態。

```javascript=
type Human = {
    age: number
    name: string
    spokenLanguage: string
}

let human = { age: 18, spokenLanguage: "English" };
const human2 = human as Human;
console.log(human2.name.toUpperCase()); 
//Error: Cannot read property 'toUpperCase' of null
```
- 參考資料：
1. https://medium.com/@bsalwiczek/type-annotation-vs-assertion-in-typescript-one-important-difference-4f4df715b5fe
2. https://stackoverflow.com/questions/47994926/detailed-differences-between-type-annotation-variable-type-and-type-assertion
3. https://ithelp.ithome.com.tw/articles/10217384
4. https://basarat.gitbook.io/typescript/type-system/type-assertion
5. https://medium.com/%E5%89%B5%E9%A0%86%E7%A7%91%E6%8A%80/%E7%AD%86%E8%A8%98-typescript-assertion-18fc95b69d9a
    
    
<br>
<br>

==Type & Interface==
---
- ### Type - 可以用來宣告Primitive type及物件、函式
```javascript=
type Name = string;
  
type NameResolver = () => string;

type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    } else {
        return n();
    }
}

type User = {
  name: string
  age?: number
};

type SetUser = (name: string, age: number)=> void;
```
- ### Type - 可以用來宣告物件、函式
```javascript=
interface User {
  name: string
  age: number
}

interface SetUser {
  (name: string, age: number): void;
}

interface Alarm {
    alert();
}
```

- ### Type & Interface
  - 繼承寫法不同
  - 宣告類型差異
  - https://ms0223900.medium.com/typescript-%E4%B8%8D%E8%83%BD%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84%E5%8A%9F%E8%83%BD-ed020f4f7ef3
    
```javascript=
//type extends type
type myName = {
    name:string;
}

type myAge = myName & {
    age:number;
}

//interface extends interface
interface myName {
    name:string;
}

interface myAge extends myName { 
  age: number; 
}


// type extends interface
interface myName { 
  name: string; 
}

type myAge = myName & { 
  age: number; 
}


//interface extends type
type myName = {
    name:string;
}

interface myAge extends myName {
    age:number;
}
```
```javascript=
//type 可以宣告聯合類型
interface myName {
    ...;
}
interface myAge {
    ...;
}

type Pet = myName | myAge ;


// type可以選告基本類型
type Name = string
    
    
    

//interface 實現擴充
interface User {
  name: string
  age: number
}

interface User {
  sex: string
}
    
    
// 可以用 implements 被class多重實現
interface Alarm {
    alert();
}

interface Light {
    lightOn();
    lightOff();
}

class Car implements Alarm, Light {
    alert() {
        ...;
    }
    lightOn() {
        ...;
    }
    lightOff() {
        ...;
    }
}
```

<br>
<br>

==Function==
---
 - 函式表示式（Function Expression）
```javascript=
let greeting : Function
greeting = () => {
    console.log("hello") 
}

const add = (a :number, b :number, c :number|string = 10) => {
    console.log(a+b)
    console.log(c)
}

add(8,9)  //17  10  沒有傳入第三個參數，預設數字為10
add(8,9,20)  //17, 20
add(8,9,'20') //17  '20'

```
 - 函式宣告（Function Declaration）
```javascript=
function wrapInArray(obj: string | string[]) {
    if (typeof obj === "string") {
        return [obj];
    }else{
        return obj;
    }
  }
console.log(wrapInArray('1223'))   //['1223']
console.log(wrapInArray(['123']))  //['123']
```
- 可選(==需寫在最後一個參數==)
```javascript=

function Person(Name: string, Age: number , Gender?: string) {
    if (Name) {
        return Name + 'is ' + Age + 'years old';
    } else {
        return Name + 'is a' + Age + 'years old' + Gender;
    }
}

let tomcat = Person('Tom', 10); 
let tom = Person('Tom', 20, 'boy'); 
console.log(tomcat);  //Tom is 10 years old
console.log(tom);     //Tom is a 20 years old boy
```
- Function Signature : 定義function中傳進去的參數跟回傳的type屬性
```javascript=
let logDetails : (obj:{name:string, age:number}) => void;
type person = {name: string , age:number} ;

logDetails = (obj:person) => {
    console.log(`${obj.name} is ${obj.age} years old`)
}

logDetails({name:"zoe", age:20}) // zoe is 20 years old
logDetails({name:"John", age:30}) // John is 30 years old
```
<br>
<br>

==Object==
---
```javascript=
let zoeOne : object;
zoeOne = {
    name : "zoecheng",
    gender : "female",
}

let zoeTwo : {
    name : string,
    age : number
}

zoeTwo = {name:"Jack", age:30}
```
<br>
<br>

==Class==
---
- 實現 interface
```javascript=
//interface file
export interface Hasformatter{
    format() :string;
}
```
```javascript=
// implements to class

interface Hasformatter{
    format() :string;
}

class Payment implements Hasformatter{
    name : string ;
    details : string;
    amount : number;

    constructor(a: string,b: string, c:number){
        this.name =a;
        this.details=b;
        this.amount=c
    }
    format(){
        return `${this.name} is spend ${this.amount} for ${this.details}`;
    }
}

let a = new Payment('Zoe','shopping',300);
console.log(a.format())  //Zoe is spend 300 for shopping
```
 - public : 預設，class內外皆可以存取及修改值。
```javascript=
//public 
    
class Animal {
    public name;
    public constructor(name) {
        this.name = name;
    }
}

let a = new Animal("Dog");
console.log(a.name); // Dog，可以在class外部存取

    
a.name = 'Cat';
console.log(a.name); // Cat，也可以在class外部修改值
```
 <br>  
    
 - private ： ==只能==在宣告它的類別的==內部==讀取但及修改。

```javascript=
//privite
    
class Property {
    private firstName: string;
  
    constructor() {
      this.firstName = 'Jimmy';
    }
  }
  
  const propertyInstance = new Property();
  console.log(propertyInstance.firstName);  //只能在Property中存取
```
<br>
    
 - protected ：和private區別是它在==子類別==中也是允許被讀取及修改的。
```javascript=
//protected
class Property {
    protected firstName: string;
  
    constructor() {
      this.firstName = 'Jimmy';
    }

}
    
class PropertyTwo extends Property {
    sayHi(){
        console.log(this.firstName)
        return this.firstName
    }
}
  
  const propertyInstance = new Property();
  const propertyTwoInstance = new PropertyTwo();
    
  console.log(propertyInstance.firstName);  //Error :只能在Property中存取
  console.log(propertyTwoInstance.sayHi()); //Line: 13 Jimmy
```
<br>
<br>

==Generics 泛型==
---
 - 定義function、interfaces或class的時候，不一開始指定傳入傳出型別，用<T>取代指定型別，在傳入時才指定型別。
    
```javascript=
function ArrayOne<T>(length: number, value: T): Array<T> {
    let result: T[] = []
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

console.log(ArrayOne<string>(3, 'x')) // ['x', 'x', 'x']
console.log(createArray(5, 3));  //[3,3,3,3,3]
```

```javascript=
// 泛型 + interface
interface ArrayTwo {
    <T>(length: number, value: T): Array<T>;
}

let CreateArray: ArrayTwo;
CreateArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

console.log(CreateArray(3, 'x')); // ['x', 'x', 'x']
```

```javascript=
//當指定<T> 當型別時，一開始不知道卻謝型別無法使用length屬性
//需加上interface使涵蓋length屬性才適用arg.length 

interface Length { 
    length : number
}

function StringLength <T extends Length>(arg:T): number {
    console.log(arg.length) // 4
    return(arg.length)
     
}
StringLength("test")
```
<br>
<br>



