2020還不會TypeScript?

什麼是TypeScript

TypeScript 是 JavaScript 的類型的超集,它能夠編譯成純 JavaScript。編譯出來的 JavaScript 能夠運行在任何瀏覽器上。TypeScript 編譯工具能夠運行在任何服務器和任何系統上。javascript

爲何選擇TypeScript

JavaScript是一門弱類型/動態類型腳本語言。過於靈活,沒有類型檢查。在大型項目中代碼愈來愈複雜,JavaScript這種靈活的優點就變成了短板html

TypeScript 增長了代碼的可讀性和可維護性

  • 類型系統其實是最好的文檔,大部分的函數看看類型的定義就能夠知道如何使用了
  • 能夠在編譯階段就發現大部分錯誤,這總比在運行時候出錯好
  • 加強了編輯器和 IDE 的功能,包括代碼補全、接口提示、跳轉到定義、重構等

TypeScript 很是包容

  • TypeScript 是 JavaScript 的超集,.js 文件能夠直接重命名爲 .ts 便可
  • 即便不顯式的定義類型,也可以自動作出類型推論
  • 能夠定義從簡單到複雜的幾乎一切類型
  • 即便 TypeScript 編譯報錯,也能夠生成 JavaScript 文件
  • 兼容第三方庫,即便第三方庫不是用 TypeScript 寫的,也能夠編寫單獨的類型文件供 TypeScript 讀取

TypeScript 的缺點

  • 有必定的學習成本,須要理解接口(Interfaces)、泛型(Generics)、類(Classes)、枚舉類型(Enums)等前端工程師可能不是很熟悉的概念
  • 可能和一些庫結合的不是很完美
  • 對於小型項目會有額外的開發成本

安裝、使用TypeScript

TypeScript 的命令行工具安裝方法以下:前端

npm install -g typescript
複製代碼

以上命令會在全局環境下安裝 tsc 命令,安裝完成以後,咱們就能夠在任何地方執行 tsc 命令了。java

注意:mac 的話 須要加上sudonode

sudo npm install -g typescript
複製代碼

Hello TypeScriptreact

首先建立一個01-ts.ts.ts文件git

將如下代碼複製到 01-ts.ts 中:es6

const hello = name => {
 console.log(`hello, ${name}`)  } hello ('TS') 複製代碼

而後執行github

tsc 01-ts.ts
複製代碼

這時候會生成一個編譯好的文件 01-ts.ts:web

var hello = function (name) {
 console.log("hello, " + name); }; hello('TS');  複製代碼

TypeScript 中,使用 : 指定變量的類型,: 的先後有沒有空格均可以。

上述例子中,咱們用 : 指定name 參數類型爲 string。可是編譯爲 js 以後,並無什麼檢查的代碼被插入進來。

TypeScript 只會進行靜態檢查,若是發現有錯誤,編譯的時候就會報錯。

const hello = (name:string) => {
 console.log(`hello, ${name}`)  } hello (1) 複製代碼

編輯器中會提示錯誤,編譯的時候也會出錯:

01-ts.ts:5:8 - error TS2345: Argument of type '1' is not assignable to parameter of type 'string'.
 5 hello (1)  ~ 複製代碼

就算出現錯誤仍是會生成js文件:

var hello = function (name) {
 console.log("hello, " + name); }; hello(1);  複製代碼

TypeScript 配置文件

開始使用 tsconfig.json 是一件比較容易的事,你僅僅須要寫下:

{}
複製代碼

或者使用 tsc --init建立tsconfig.json

在項目的根目錄下建立一個空 JSON 文件。經過這種方式,TypeScript 將 會把此目錄和子目錄下的全部 .ts 文件做爲編譯上下文的一部分,它還會包含一部分默認的編譯選項。

編譯選項

你能夠經過 compilerOptions 來定製你的編譯選項:

{
 "compilerOptions": {   /* 基本選項 */  "target": "es5", // 指定 ECMAScript 目標版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'  "module": "commonjs", // 指定使用模塊: 'commonjs', 'amd', 'system', 'umd' or 'es2015'  "lib": [], // 指定要包含在編譯中的庫文件  "allowJs": true, // 容許編譯 javascript 文件  "checkJs": true, // 報告 javascript 文件中的錯誤  "jsx": "preserve", // 指定 jsx 代碼的生成: 'preserve', 'react-native', or 'react'  "declaration": true, // 生成相應的 '.d.ts' 文件  "sourceMap": true, // 生成相應的 '.map' 文件  "outFile": "./", // 將輸出文件合併爲一個文件  "outDir": "./", // 指定輸出目錄  "rootDir": "./", // 用來控制輸出目錄結構 --outDir.  "removeComments": true, // 刪除編譯後的全部的註釋  "noEmit": true, // 不生成輸出文件  "importHelpers": true, // 從 tslib 導入輔助工具函數  "isolatedModules": true, // 將每一個文件作爲單獨的模塊 (與 'ts.transpileModule' 相似).   /* 嚴格的類型檢查選項 */  "strict": true, // 啓用全部嚴格類型檢查選項  "noImplicitAny": true, // 在表達式和聲明上有隱含的 any類型時報錯  "strictNullChecks": true, // 啓用嚴格的 null 檢查  "noImplicitThis": true, // 當 this 表達式值爲 any 類型的時候,生成一個錯誤  "alwaysStrict": true, // 以嚴格模式檢查每一個模塊,並在每一個文件里加入 'use strict'   /* 額外的檢查 */  "noUnusedLocals": true, // 有未使用的變量時,拋出錯誤  "noUnusedParameters": true, // 有未使用的參數時,拋出錯誤  "noImplicitReturns": true, // 並非全部函數裏的代碼都有返回值時,拋出錯誤  "noFallthroughCasesInSwitch": true, // 報告 switch 語句的 fallthrough 錯誤。(即,不容許 switch 的 case 語句貫穿)   /* 模塊解析選項 */  "moduleResolution": "node", // 選擇模塊解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)  "baseUrl": "./", // 用於解析非相對模塊名稱的基目錄  "paths": {}, // 模塊名到基於 baseUrl 的路徑映射的列表  "rootDirs": [], // 根文件夾列表,其組合內容表示項目運行時的結構內容  "typeRoots": [], // 包含類型聲明的文件列表  "types": [], // 須要包含的類型聲明文件名列表  "allowSyntheticDefaultImports": true, // 容許從沒有設置默認導出的模塊中默認導入。   /* Source Map Options */  "sourceRoot": "./", // 指定調試器應該找到 TypeScript 文件而不是源文件的位置  "mapRoot": "./", // 指定調試器應該找到映射文件而不是生成文件的位置  "inlineSourceMap": true, // 生成單個 soucemaps 文件,而不是將 sourcemaps 生成不一樣的文件  "inlineSources": true, // 將代碼與 sourcemaps 生成到一個文件中,要求同時設置了 --inlineSourceMap 或 --sourceMap 屬性   /* 其餘選項 */  "experimentalDecorators": true, // 啓用裝飾器  "emitDecoratorMetadata": true // 爲裝飾器提供元數據的支持  } } 複製代碼

配置文件配好後 就能夠直接使用tsc 進行編譯

TypeScript 做用域問題

在默認狀況下,當你開始在一個新的 TypeScript 文件中寫下代碼時,它處於全局命名空間中。如在 foo.ts 裏的如下代碼。

const foo = 123;
複製代碼

若是你在相同的項目裏建立了一個新的文件 bar.ts,TypeScript 類型系統將會容許你使用變量 foo,就好像它在全局可用同樣:

const bar = foo; // allowed
複製代碼

毋庸置疑,使用全局變量空間是危險的,由於它會與文件內的代碼命名衝突。咱們推薦使用下文中將要提到的文件模塊

解決這個咱們只須要在文件最後 使用export {} 這樣每一個文件就是一個模塊,單獨的做用域

TypeScript 基礎

TypeScript 原始數據類型

// 數字,2、8、十六進制都支持
let num:number =0 let num2:number=0xf00a  // 字符串 let v:string="a" //let name:string="liu" // 這裏會提示 'name' was also declared here  // 緣由是在默認狀態下,typescript 將 DOM typings 做爲全局的運行環境,因此當咱們聲明 name時, 與 DOM 中的全局 window 對象下的 name 屬性出現了重名。 // const a:string=null  // 這裏是在tsconfig.json 配置了嚴格模式 "strict": true 改爲false 就行了  const c:boolean=false // void只能存放null/undefined 注意:在嚴格模式下只能是 undefined const d:void=undefined  const f:null=null  const g:undefined=undefined // 這裏報錯 'Symbol' only refers to a type, but is being used as a value here. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later. //是由於Symbol是es2015纔有的 ts使用的標準庫沒有 在tsconfig.json設置 "lib": ["es2015","DOM"] //之後遇到內置方法報錯的時候可能就是標準庫的關係 在在tsconfig.json設置 "lib" 就行了 const h:symbol=Symbol()   複製代碼

TypeScript Object類型

const foo:object =  {}//能夠是 function (){} //[]//{}
const obj:{foo:number,bar:string}={foo:1,bar:'w'} // 這裏只是簡單的定義,跟專業的須要用接口來定義  export { } //確保跟其餘成員沒有衝突 複製代碼

TypeScript 數組類型

// 寫法1
const arr1:Array<number>=[1,2,3] // 寫法2 const arr2:number[]=[1,2,3]  let arr3:string[] = ["1","2"] let arr4:Array<string> = ["1","2"]  // 小案例 // 若是是 JS,須要判斷是否是每一個成員都是數字 // 使用 TS,類型有保障,不用添加類型判斷 function sum (...args: number[]) {  return args.reduce((prev, current) => prev + current, 0) }  sum(1, 2, 3) // => 6 複製代碼

TypeScript 元組類型

// 元組(Tuple)
 export {} // 確保跟其它示例沒有成員衝突  const tuple: [number, string] = [18, 'zce']  // const age = tuple[0] // const name = tuple[1]  const [age, name] = tuple  // ---------------------  const entries: [string, number][] = Object.entries({  foo: 123,  bar: 456 })  const [key, value] = entries[0] // key => foo, value => 123 複製代碼

TypeScript 枚舉類型

枚舉是對JavaScript標準數據類型集的擴充,常被用來限定在必定範圍內取值的場景,如一週只能有七天,顏色限定爲紅綠藍等。串的枚舉。咱們能夠用enum來實現。

簡單例子

// 枚舉
// 注意 enum 是採用 = 賦值  // enum PostStatus { // Draft = 0, // Unpublished = 1, // Published = 2 // }  // 字符串枚舉 // enum PostStatus { // Draft = "a", // Unpublished = "b", // Published = "c" // }  //---------------------------- // 能夠不用賦值 默認從0開始  // enum PostStatus { // Draft , //0 // Unpublished , //1 // Published //2 // }  //---------------------------- // 給第一個賦值 後面的會在第一個基礎上自加  // enum PostStatus { // Draft =6, //6 // Unpublished , //7 // Published //8 // }    複製代碼

常量枚舉

// 常量枚舉
const enum PostStatus {  Draft , //0  Unpublished , //1  Published //2 } const post ={ status:PostStatus.Draft } // 編譯後 var post = {  status: 1 /* Draft */ }; 複製代碼

我一般用 = 1 初始化,由於在枚舉類型值裏,它能讓你作一個安全可靠的檢查。

TypeScript 函數類型

// 簡單案例
 // function func1 (a: number, b: number): string { // return 'func1' // }  // func1(100, 200)   // 默認值或者能夠選參數 // b: number=1 // b?: number 問號標示可選 // function func1 (a: number, b: number=1): string { // return 'func1' // }  // func1(100, 200)  // 任意個數的參數 使用es6擴展運算符  // function func1 (...res:number): string { // return 'func1' // }  // func1(100, 200) 複製代碼

TypeScript 任意(any)類型

// 任意類型(弱類型)
 export {} // 確保跟其它示例沒有成員衝突  function stringify (value: any) {  return JSON.stringify(value) }  stringify('string')  stringify(100)  stringify(true)  let foo: any = 'string'  foo = 100  foo.bar()  // any 類型是不安全的  複製代碼

TypeScript 隱式類型推斷

若是沒有明確的指定類型,那麼 TypeScript 會依照類型推論(Type Inference)的規則推斷出一個類型。

案列

let foo = 123; // foo 是 'number'
let bar = 'hello'; // bar 是 'string'  foo = bar; // Error: 不能將 'string' 賦值給 `number`  //數組 const bar = [1, 2, 3]; bar[0] = 'hello'; // Error:不能把 'string' 類型賦值給 'number' 類型  //對象 const foo = {  a: 123,  b: 456 };  foo.a = 'hello'; // Error:不能把 'string' 類型賦值給 'number' 類型 複製代碼

建議儘可能每一個變量都添加明確的類型,便於後期更直觀的理解代碼

注意

當心使用參數

若是類型不能被賦值推斷出來,類型也將不會流入函數參數中。例如以下的一個例子,編譯器並不知道 foo 的類型,所它也就不能推斷出 a 或者 b 的類型。

const foo = (a, b) => {
 /* do something */ }; 複製代碼

然而,若是 foo 添加了類型註解,函數參數也就能被推斷(a,b 都能被推斷爲 number 類型):

type TwoNumberFunction = (a: number, b: number) => void;
const foo: TwoNumberFunction = (a, b) => {  /* do something */ }; 複製代碼

當心使用返回值

儘管 TypeScript 通常狀況下能推斷函數的返回值,可是它可能並非你想要的。例如以下的 foo 函數,它的返回值爲 any:

function foo(a: number, b: number) {
 return a + addOne(b); }  // 一些使用 JavaScript 庫的特殊函數 function addOne(a) {  return a + 1; } 複製代碼

這是由於返回值的類型被一個缺乏類型定義的 addOne 函數所影響(a 是 any,因此 addOne 返回值爲 any,foo 的返回值是也是 any)。

TypeScript 進階

TypeScript 類型斷言

在某些狀況TS沒法檢測出類型,須要你告訴它。TypeScript 類型斷言用來告訴編譯器你比它更瞭解這個類型,而且它不該該再發出錯誤

// 類型斷言
 export {} // 確保跟其它示例沒有成員衝突  // 假定這個 nums 來自一個明確的接口 const nums = [110, 120, 119, 112]  const res = nums.find(i => i > 0)  // res TypeScript 是不知道是什麼類型的 全部須要你告訴它 // const square = res * res  // 推薦使用 as const num1 = res as number  const num2 = <number>res // JSX 下不能使用 複製代碼

注意: 類型斷言不是類型轉換,是由於轉換一般意味着某種運行時的支持。可是,類型斷言純粹是一個編譯時語法,同時,它也是一種爲編譯器提供關於如何分析代碼的方法。

TypeScript 接口

在面嚮對象語言中,接口(Interfaces)是一個很重要的概念,它是對行爲的抽象,而具體如何行動須要由類(classes)去實現(implement)。

簡單案例

// 接口
interface Post {  title:string  content:string } // 使用接口 function printPost (post: Post) {  console.log(post.title)  console.log(post.content)  } printPost({title:'234',content:'23'})  複製代碼

可選成員/只讀成員/任意成員

// ? 標示可選 // interface Post { // title:string // content:string, // subtitle?:string // } // // 使用接口 // function printPost (post: Post) { // console.log(post.title) // console.log(post.content)  // }  // readonly 只讀 interface Post {  title:string  content:string,  subtitle?:string,  readonly sun:string } // 使用接口 function printPost (post: Post) {  console.log(post.title)  console.log(post.content)  }   // 任意類型的屬性 interface Post {  [prop:string]:string } // 使用接口 const cache:Post={  name:'sdffs',  title:'s' }  複製代碼

TypeScript 類的使用

簡單例子

export { }
class Person {  // 首先先定義類型  name: string  age: string  constructor(name: string, age: string) {   this.name = name  this.age = age  }  sayHi(msg:string):void{  console.log(`${this.name}${msg}`);   }  } 複製代碼

類的訪問修飾符

  • private : 私有
  • public :公共
  • protected :受保護的
class Person {
 // 首先先定義類型  public name: string //公共  private age: string // 私有  protected gender: string //  constructor(name: string, age: string) {   this.name = name  this.age = age  this.gender = 'sss'  }  sayHi(msg: string): void {  console.log(`${this.name}${msg}`);   }  }  class Student extends Person {  constructor(name: string, age: string) {  super(name, age)  console.log(this.gender) // 能夠訪問到   }  } const tom = new Person('tom', '18') // console.log(tom.age); //訪問不到 // console.log(tom.gender); //也訪問不到  複製代碼

constructor 加修飾符

class Student extends Person {
 private constructor (name: string, age: number) {  super(name, age)  console.log(this.gender)  }   static create (name: string, age: number) {  return new Student(name, age)  } }  const tom = new Person('tom', 18) console.log(tom.name) // console.log(tom.age) // console.log(tom.gender)  //不能直接經過 new來創造對象 const jack = Student.create('jack', 18)  複製代碼

只讀屬性

使用`readonly`來定義只讀,若是有修飾符的話 跟在修飾符後面
class Person {  // 首先先定義類型  public name: string //公共  private age: string // 私有  // 只讀成員 readonly  protected readonly gender: boolean  constructor(name: string, age: string) {   this.name = name  this.age = age  this.gender = 'sss'  }  sayHi(msg: string): void {  console.log(`${this.name}${msg}`);   }  } 複製代碼

類與接口

實現(implements)是面向對象中的一個重要概念。通常來說,一個類只能繼承自另外一個類,有時候不一樣類之間能夠有一些共有的特性,這時候就能夠把特性提取成接口(interfaces),用 implements 關鍵字來實現。這個特性大大提升了面向對象的靈活性。

舉例來講,門是一個類,防盜門是門的子類。若是防盜門有一個報警器的功能,咱們能夠簡單的給防盜門添加一個報警方法。這時候若是有另外一個類,車,也有報警器的功能,就能夠考慮把報警器提取出來,做爲一個接口,防盜門和車都去實現它:

export{} // 先定義接口  interface Eat {  // 定義方法類型  eat (food:string):void  }  interface Run {  // 定義方法類型  run (distance:string):void  }  class Person implements Eat,Run{  eat(food:string):void{  console.log(food);   }  run (distance:string):void {  console.log(distance);  }  } 複製代碼

抽象類

使用abstract來定義 抽象類

abstract class Animal {
 eat (food: string): void {  console.log(`呼嚕呼嚕的吃: ${food}`)  }  // 抽象方法不須要方法體  abstract run (distance: number): void }  class Dog extends Animal {  run(distance: number): void {  console.log('四腳爬行', distance)  }  }  const d = new Dog() d.eat('嗯西馬') d.run(100) 複製代碼

不能直接經過 new Animal() 建立實例

TypeScript泛型

泛型(Generics)是指在定義函數、接口或類的時候,不預先指定具體的類型,而在使用的時候再指定類型的一種特性。

// 泛型
 export {} // 確保跟其它示例沒有成員衝突  function createNumberArray (length: number, value: number): number[] {  const arr = Array<number>(length).fill(value)  return arr }  function createStringArray (length: number, value: string): string[] {  const arr = Array<string>(length).fill(value)  return arr }  function createArray<T> (length: number, value: T): T[] {  const arr = Array<T>(length).fill(value)  return arr }  // const res = createNumberArray(3, 100) // res => [100, 100, 100]  const res = createArray<string>(3, 'foo') 複製代碼

類型聲明

有一些三方的庫 引入不知道是什麼類型 可使用declarelai 聲明類型

還有些第三方庫會有專門的類型聲明文件

import { camelCase } from 'lodash'
import qs from 'query-string'  qs.parse('?key=value&key2=value2')  // declare function camelCase (input: string): string  const res = camelCase('hello typed') 複製代碼

參考

深刻理解 TypeScript

TypeScript 入門教程

相關文章
相關標籤/搜索