首頁 > 軟體

TypeScript保姆級基礎教學

2022-07-01 18:02:04

什麼是 TypeScript?

TypeScript,簡稱 ts,是微軟開發的一種靜態的程式語言,它是 JavaScript 的超集。 那麼它有什麼特別之處呢?

  • 簡單來說,js 有的 ts 都有,所有js 程式碼都可以在 ts 裡面執行。
  • ts 支援型別支援,ts = type +JavaScript。

那麼 ts 和 js 有什麼區別呢?

JavaScript 屬於動態程式語言,而ts 屬於靜態程式語言。

js:邊解釋邊執行,錯誤只有在執行的時候才能發現

ts:先編譯再執行,在寫的時候就會發現錯誤了(ts不能直接執行,需要先編譯成 js )

ts 完全支援 js ,可以直接轉換

ts 有型別支援,有強大的程式碼型別提示

相信大家現在對 ts 有一個基本的瞭解了,那麼它應該怎麼使用呢? 首先先做一些簡單的準備工作:

  • 下載一個全域性的包
npm i  -g  typescript
#或者
yarn  global add  typescript
  • 開啟cmd 輸入命令 tsc -v 檢視包是否下載成功

當 cmd 輸入指令出現版本號,就說明下載成功啦

首先開啟 vscode, 新建一個 .ts檔案。

如 hello.ts

接下來就可以書寫 ts 的程式碼啦!

  • 新建檔案,如 hello.ts。(ts檔案的字尾名都是 .ts哦)
  • 書寫程式碼

編譯程式碼:在終端輸入 tsc ./hello.ts, 會自動生成一個js 檔案,接下來就可以用 node 命名來執行js 的檔案,在瀏覽器開啟看效果啦

如果不想下載包的話,也可以線上執行,連結如下: 線上執行 接下來了解一下 ts 的型別註解

什麼是型別註解?

就是給變數新增型別約束,可以顯示標記出程式碼中的意外行為,從而降低了發生錯誤的可能性 語法:

let 變數名: 型別 = 初始值
let age: number = 18

這樣寫有啥好處捏?更好的規定了資料的型別,避免了不必要的錯誤。 如果你不小心寫錯了,他會直接提示你型別錯誤哦。是不是很貼心呢?

程式碼中的 : number 就是型別註解。

注:這裡的程式碼錯誤提示,我是下載了一個外掛才有的。如果大家也想有提示的話,可以在 vscode 裡面下載。我把外掛的截圖貼在下面。

ts 的型別

ts 的常用基礎型別分為兩種: js 已有型別

原始型別:number/string/boolean/null/undefined/symbol

物件型別:object(包括,陣列、物件、函數等物件)

ts 新增型別

  • 聯合型別
  • 自定義型別(型別別名)
  • 介面
  • 元組
  • 字面量型別
  • 列舉
  • void
  • any
  • 等等

原始型別: number/string/boolean/null/undefined/symbol 語法比較簡單

基本和 js 差別不大

// 數值型別
let age: number = 18
// 字串型別
let myName: string = '小花'
// 布林型別
let isLoading: boolean = false
// undefined
let un: undefined = undefined
// null
let timer:null = null
// symbol
let uniKey:symbol = Symbol()

型別推論 在 TS 中,某些沒有明確指定型別的情況下,TS 的型別推論機制會自動提供型別。好處:由於型別推論的存在,有些情況下的型別註解可以省略不寫 有如下兩種場景:

  • 宣告變數並初始化時

  • 決定函數返回值時

已知函數的兩個引數都是number型別,那麼該函數的返回值也是 number 型別。

聯合型別

需求:如何定義一個變數可以是null也可以是 number 型別? 這個時候,前面所學的已經不能滿足我們的需求了,就需要用到一個新的型別 - 組合型別。 語法:

let 變數: 型別1 | 型別2 | 型別3 .... = 初始值
let arr1 :number | string = 1 // 可以寫兩個型別

注意: 這裡的 | 豎線,在 TS 中叫做聯合型別,即:由兩個或多個其他型別組成的型別,表示可以是這些型別中的任意一種。不要和 js 中的 || 搞混哦。 應用場景: 定時器id

// | 聯合型別  變數可以是兩種型別之一
let timer:number|null = null
timer = setTimeout()

型別別名

在我們定義型別的時候,有時候自己定義的型別名往往很長,這個時候就需要在定義個別名,方便書寫。 語法:

type 別名 = 型別
type s = string // 定義
const str1:s = 'abc'
const str2:string = 'abc'

作用:

  • 給型別起別名
  • 定義了新型別

使用場景:給複雜的型別起個別名

 type NewType = string | number
 let a: NewType = 1
 let b: NewType = '1'

注意:別名可以是任意的合法字串,一般首字母大寫

陣列型別

語法:

// 寫法1:
let 變數: 型別[] = [值1,...]:
let numbers: number[] = [1, 3, 5] 
//  numbers必須是陣列,每個元素都必須是數位
// 寫法2:
let 變數: Array<型別> = [值1,...]
let strings: Array<string> = ['a', 'b', 'c'] 
//  strings必須是陣列,每個元素都必須是字串

函數

函數涉及的型別實際上指的是:函數引數和返回值的型別

定義單個函數

語法:

// 普通函數
function 函數名(形參1: 型別=預設值, 形參2:型別=預設值,...): 返回值型別 { }
// 宣告式實際寫法:
function add(num1: number, num2: number): number {
  return num1 + num2
}
// 箭頭函數
const 函數名(形參1: 型別=預設值, 形參2:型別=預設值, ...):返回值型別 => { }
const add2 = (a: number =100, b: number = 100): number =>{
   return a + b
 }
 // 注意: 箭頭函數的返回值型別要寫在引數小括號的後面
add(1,'1') // 報錯

統一定義函數格式

當函數的型別一致時,寫多個就會顯得程式碼冗餘,所以需要統一定義函數的格式 如下所示:

const add2 = (a: number =100, b: number = 100): number => {
    return a + b
  }
function add1 (a:number = 100 , b: number = 200): number {
    return a + b
  }
// 這裡的 add1 和 add2 的引數型別和返回值一致,
// 那麼就可以統一定義一個函數型別
type Fn = (n1:number,n2:number) => number 
const add3 : Fn = (a,b)=>{return a+b }
// 這樣書寫起來就簡單多啦

函數返回值型別void

在 ts 中,如果一個函數沒有返回值,應該使用 void 型別

function greet(name: string): void {  console.log('Hello', name)  //}

可以用到void 有以下幾種情況

  • 函數沒寫return
  • 只寫了 return, 沒有具體的返回值
  • return 的是 undefined
// 如果什麼都不寫,此時,add 函數的返回值型別為: void
const add = () => {}
// 如果return之後什麼都不寫,此時,add 函數的返回值型別為: void
const add = () => { return }
const add = (): void => {
  // 此處,返回的 undefined 是 JS 中的一個值
  return undefined
}
// 這種寫法是明確指定函數返回值型別為 void,與上面不指定返回值型別相同
const add = (): void => {}

那麼就有人好奇,既然return undefined,那麼為什麼不可以直接在返回值那裡寫 :undefined 呢? 如果函數沒有指定返回值,呼叫結束之後,值是undefined的,但是不能直接宣告返回值是undefined

function add(a:number, b:number): undefined { // 這裡會報錯
  console.log(a,b)
}

函數-可選引數

使用函數實現某個功能時,引數可以傳也可以不傳。

例如:陣列的 slice 方法,可以 slice() 也可以 slice(1) 還可以 slice(1, 3) 那麼就可以定義可選引數 語法:

//可選引數:在可選引數名的後面新增 ?(問號)
function slice (a?: number, b?: number) {
    // ? 跟在引數名字的後面,表示可選的引數
    // 注意:可選引數只能在 必須引數的後面
    // 如果可選引數在必選引數的前面,會報錯
    console.log(111);
  }
  slice()
  slice(1)
  slice(1,2)
}

可選和預設值的區別

相同點: 呼叫函數時,可以少傳引數

區別:設定了預設值之後,就是可選的了,不寫就會使用預設值; 可選的引數一定有值。

注意:它們不能一起使用。優先使用預設值

物件型別-單獨使用

格式: 方法有兩種寫法: 普通函數 和 箭頭函數

const 物件名: {
  屬性名1:型別1,
  屬性名2?:型別2,
  方法名1(形參1: 型別1,形參2: 型別2): 返回值型別,
  方法名2:(形參1: 型別1,形參2: 型別2) => 返回值型別
} = { 屬性名1: 值1,屬性名2:值2  }

物件型別-型別別名

// 建立型別別名
type Person = {
  name: string,
  age: number
  sayHi(): void
}
// 使用型別別名作為物件的型別:
let person: Person = {
  name: '小花',
  age: 18
  sayHi() {}
}

介面

當一個物件型別被多次使用時,有如下兩種方式來來描述物件的型別,以達到複用的目的:

  • 型別別名,type
  • 介面,interface

語法:

interface 介面名  {屬性1: 型別1, 屬性2: 型別2}
// 這裡用 interface 關鍵字來宣告介面
interface IGoodItem  {
	// 介面名稱(比如,此處的 IPerson),可以是任意合法的變數名稱,推薦以 `I` 開頭
   name: string, price: number, func: ()=>string
}
// 宣告介面後,直接使用介面名稱作為變數的型別
const good1: IGoodItem = {
   name: '手錶',
   price: 200,
   func: function() {
       return '看時間'
   }
}
const good2: IGoodItem = {
    name: '手機',
    price: 2000,
    func: function() {
        return '打電話'
    }
}

介面和型別 的區別 interface(介面)和 type(型別別名)的對比:

  • 相同點:都可以給物件指定型別
  • 不同點:
    • 介面,只能為物件指定型別。它可以繼承。
    • 型別別名,不僅可以為物件指定型別,實際上可以為任意型別指定別名

先有的 interface,後有的 type,推薦使用 type

// 介面的寫法-------------
interface IPerson {
	name: string,
	age: number
}
const user1:IPerson = {
	name: 'a',
	age: 20
}
// type的寫法-------------
type Person  = {
	name: string,
	age: number
}
const user2:Person = {
	name: 'b',
	age: 20
}

介面繼承

如果兩個介面之間有相同的屬性或方法,可以將公共的屬性或方法抽離出來,通過繼承來實現複用 語法:

interface 介面2 extends 介面1 {
 屬性1: 型別1, // 介面2中特有的型別 
 }
interface a { x: number; y: number }
// 繼承 a
// 使用 extends(繼承)關鍵字實現了介面
interface b extends a {
  z: number
}
// 繼承後,b 就有了 a 的所有屬性和方法(此時,b 同時有 x、y、z 三個屬性)

元組

元組是一種特殊的陣列。有兩點特殊之處

  • 它約定了的元素個數

  • 它約定了特定索引對應的資料型別

舉個例子: 就拿 react 裡面的 useState來舉例:

function useState(n: number): [number, (number)=>void] {
        const setN = (n1) => {
            n = n1
        }
        return [n, setN]
    }
const [num ,setNum] = useState(10)

字面量型別

例如下面的程式碼

let str1 = 'hello TS'
const str2 = 'hello TS'

大家可以猜一下,str1 是什麼型別的,str2 是什麼型別?不要偷偷看答案哦。

這裡是正確答案: str1 的型別為 string 型別,str2 的型別為 Hello TS型別

這是為啥呢?

  • str1 是一個變數(let),它的值可以是任意字串,所以型別為:string
  • str2 是一個常數(const),它的值不能變化只能是 'hello TS',所以,它的型別為:'hello TS'

注意:此處的 'Hello TS',就是一個字面量型別,也就是說某個特定的字串也可以作為 TS 中的型別

這時候就有人好奇了,那字面量型別有啥作用呢? 字面量型別一般是配合聯合型別一起使用的, 用來表示一組明確的可選值列表。 例如下面的例子:

type Gender = 'girl' | 'boy'
// 宣告一個型別,他的值 是 'girl' 或者是 'boy'
let g1: Gender = 'girl' // 正確
let g2: Gender = 'boy' // 正確
let g3: Gender = 'man' // 錯誤

可以看到會有提示,明確告訴你只能選這兩種。媽媽再也不用擔心寫錯啦

列舉

列舉(enum)的功能類似於字面量型別+聯合型別組合的功能,來描述一個值,該值只能是 一組命名常數 中的一個。 在沒有 type 的時候,大家都是用列舉比較多的,現在比較少了。 語法:

enum 列舉名 { 可取值1, 可取值2,.. }
// 使用格式:
列舉名.可取值

注意:

  • 一般列舉名稱以大寫字母開頭
  • 列舉中的多個值之間通過 ,(逗號)分隔
  • 定義好列舉後,直接使用列舉名稱作為型別註解

列舉也分數值列舉 和 字串列舉。 數值列舉: 預設情況下,列舉的值是數值。預設為:從 0 開始自增的數值 當然,也可以給列舉中的成員初始化值

enum Direction { Up = 10, Down, Left, Right }
// Down -> 11、Left -> 12、Right -> 13
enum Direction { Up = 2, Down = 3, Left = 8, Right = 16 }

字串列舉:

enum Direction {
  Up = 'UP',
  Down = 'DOWN',
  Left = 'LEFT',
  Right = 'RIGHT'
}

注意:字串列舉沒有自增長行為,因此,字串列舉的每個成員必須有初始值

any 型別

any: 任意的。當型別設定為 any 時,就取消了型別的限制。 例如:

let obj: any = { x: 0 }
obj.bar = 100
obj()
// obj 可以是任意型別
const n: number = obj

使用any的場景

  • 函數就是不挑型別。 例如,console.log() ; 定義一個函數,輸入任意型別的資料,返回該資料型別
  • 臨時使用 any 來“避免”書寫很長、很複雜的型別

還有一種隱式 any,有下面兩種情況會觸發

  • 宣告變數不提供型別也不提供預設值
  • 定義函數時,引數不給型別

注意:不推薦使用 any!這會讓 TypeScript 變為 “AnyScript”(失去 TS 型別保護的優勢)

型別介紹這邊就不一一展開了,更多介紹請看查閱官網。TypeScript官方檔案

以上就是TypeScript保姆級教學的詳細內容,更多關於TypeScript教學的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com